Deployment & Hosting Kapsamlı Rehber (Comprehensive Deployment & Hosting Guide)
Full-stack projeler için deploy, hosting, sunucu yönetimi ve production hazırlığı referansı.
Her proje sonunda deploy edilmeli
✅ Yap: Production ortamını planla, CI/CD kur, SSL sertifikası ekle, monitoring başlat
⚠️ Production hazırlığı: Checklist uygula, env değişkenlerini kontrol et, graceful shutdown ekle
❌ Yapma: .env dosyasını repoya commitlme, root kullanıcı ile uygulama çalıştırma, HTTP üzerinde production sunma
Hosting Platformları Karşılaştırması (Hosting Platforms Comparison)
Genel Bakış Tablosu (Overview Table)
| Platform | Tür | Ücretsiz Plan | Özel Domain | SSL | Auto Deploy | Ölçekleme | En Uygun |
|---|---|---|---|---|---|---|---|
| Vercel | Serverless | Var | Var | Var | Var | Otomatik | Next.js, React, statik siteler |
| Netlify | Serverless | Var | Var | Var | Var | Otomatik | Statik siteler, JAMstack |
| Railway | PaaS | Sınırlı | Var | Var | Var | Manuel | Full-stack, veritabanı ile |
| Render | PaaS | Sınırlı | Var | Var | Var | Manuel/Oto | Web servisleri, cron işler |
| VPS (Hetzner/DigitalOcean) | IaaS | Yok | Manuel | Manuel | Manuel | Manuel | Tam kontrol, özel yapılar |
| AWS (EC2/ECS/Lambda) | IaaS/PaaS | Sınırlı | Var | Var | Var | Otomatik | Büyük ölçekli, kurumsal |
Platform Seçim Kriterleri (Platform Selection Criteria)
| Kriter | Vercel/Netlify | Railway/Render | VPS | AWS |
|---|---|---|---|---|
| Maliyet (düşük trafik) | Ücretsiz | Düşük | Aylık sabit | Kullanıma göre |
| Maliyet (yüksek trafik) | Pahalı | Orta | Sabit | Değişken |
| Öğrenme eğrisi | Kolay | Kolay | Orta | Zor |
| Kontrol seviyesi | Düşük | Orta | Tam | Tam |
| Veritabanı | Harici | Dahili | Kendin kur | Managed servis |
| SSH erişimi | Yok | Sınırlı | Tam | Tam |
| Bakım gerekliliği | Yok | Düşük | Yüksek | Orta-Yüksek |
Vercel
# Vercel CLI kurulumu
npm i -g vercel
# Projeyi deploy et
vercel
# Production deploy
vercel --prod
# Ortam degiskeni ekle
vercel env add VARIABLE_NAME productionvercel.json yapılandırması:
{
"version": 2,
"builds": [
{ "src": "package.json", "use": "@vercel/next" }
],
"routes": [
{ "src": "/api/(.*)", "dest": "/api/$1" },
{ "src": "/(.*)", "dest": "/$1" }
],
"env": {
"DATABASE_URL": "@database-url"
}
}Netlify
# Netlify CLI kurulumu
npm i -g netlify-cli
# Giris yap
netlify login
# Projeyi baslat
netlify init
# Deploy
netlify deploy --prod --dir=distnetlify.toml yapılandırması:
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "20"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Railway
# Railway CLI kurulumu
npm i -g @railway/cli
# Giris ve proje olusturma
railway login
railway init
# Deploy
railway up
# Ortam degiskeni ekle
railway variables set KEY=VALUE
# Loglari gor
railway logsPM2 Detaylı Kullanım (PM2 Detailed Usage)
PM2, Node.js uygulamaları için production-grade process manager'dır.
Kurulum ve Temel Komutlar (Installation & Basic Commands)
# Global kurulum
npm install -g pm2
# Uygulama baslatma
pm2 start app.js
pm2 start app.js --name "api-server"
pm2 start app.js -i max # Cluster mode (tum CPU cekirdekleri)
pm2 start app.js -i 4 # 4 instance
# Durdurma, yeniden baslatma, silme
pm2 stop app-name
pm2 restart app-name
pm2 reload app-name # Zero-downtime reload
pm2 delete app-name
# Tum uygulamalari yonet
pm2 stop all
pm2 restart all
pm2 delete allDurum ve İzleme (Status & Monitoring)
# Uygulama listesi
pm2 list
pm2 ls
# Detayli bilgi
pm2 show app-name
pm2 describe app-name
# Canli izleme (CPU, RAM, uptime)
pm2 monit
# Dashboard (web arayuzu)
pm2 plusLog Yönetimi (Log Management)
# Tum loglari goruntule
pm2 logs
# Belirli uygulama loglari
pm2 logs app-name
# Son N satir
pm2 logs app-name --lines 200
# Log dosya konumlari
pm2 logs --json
# Loglari temizle
pm2 flush
pm2 flush app-name
# Log rotate modulu
pm2 install pm2-logrotate
# Log rotate ayarlari
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ssEcosystem Dosyası (Multi-App Configuration)
ecosystem.config.js -- birden fazla uygulamayı tek dosyayla yönetme:
module.exports = {
apps: [
{
name: "api-server",
script: "./src/server.js",
instances: "max",
exec_mode: "cluster",
watch: false,
max_memory_restart: "500M",
env: {
NODE_ENV: "development",
PORT: 3000,
},
env_production: {
NODE_ENV: "production",
PORT: 3000,
},
error_file: "./logs/api-error.log",
out_file: "./logs/api-out.log",
merge_logs: true,
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
},
{
name: "worker",
script: "./src/worker.js",
instances: 2,
exec_mode: "cluster",
cron_restart: "0 */6 * * *", // Her 6 saatte yeniden baslat
watch: false,
autorestart: true,
max_restarts: 10,
restart_delay: 5000,
env_production: {
NODE_ENV: "production",
},
},
{
name: "scheduler",
script: "./src/scheduler.js",
instances: 1,
exec_mode: "fork",
cron_restart: "0 0 * * *", // Her gun gece yarisi
env_production: {
NODE_ENV: "production",
},
},
{
name: "frontend-ssr",
script: "npm",
args: "run start",
cwd: "./frontend",
instances: 2,
exec_mode: "cluster",
env_production: {
NODE_ENV: "production",
PORT: 3001,
},
},
],
};# Ecosystem ile baslatma
pm2 start ecosystem.config.js
pm2 start ecosystem.config.js --env production
# Belirli bir uygulamayi baslat
pm2 start ecosystem.config.js --only api-server
# Tum uygulamalari yeniden yukle
pm2 reload ecosystem.config.jsStartup ve Save (Startup & Persistence)
# Sunucu yeniden basladiginda PM2'nin otomatik baslamasi
pm2 startup
# Ciktiyi kopyalayip calistir (sudo komutu verir)
# Mevcut uygulama listesini kaydet
pm2 save
# Kaydedilen listeyi geri yukle
pm2 resurrect
# Startup script'ini kaldir
pm2 unstartupPM2 vs Diğer Process Manager'lar (PM2 vs Other Process Managers)
| Özellik | PM2 | Supervisor | systemd |
|---|---|---|---|
| Dil desteği | Node.js (öncelikli), Python, Go | Herhangi bir dil | Herhangi bir dil |
| Cluster mode | Var (dahili) | Yok | Yok |
| Zero-downtime reload | Var | Yok | Sınırlı |
| Log yönetimi | Dahili + rotate | Dahili | journalctl |
| Monitoring | pm2 monit, pm2 plus | supervisorctl | systemctl status |
| Watch & restart | Var | Yok | Yok |
| Ecosystem dosyası | Var | ini dosyası | unit dosyası |
| Startup integration | pm2 startup | OS'e göre | Dahili |
| Web arayüzü | pm2 plus (bulut) | supervisord web | cockpit |
| Kaynak tüketimi | Orta | Düşük | Çok düşük |
| Kurulum kolaylığı | npm install | apt install | Dahili |
| Önerilen kullanım | Node.js projeleri | Python/Genel | Sistem servisleri |
Sunucu Proje Başlatma Komutları (Server Project Startup Commands)
Her framework için geliştirme (dev) ve production başlatma komutları.
Express.js
# --- Development ---
node app.js
nodemon app.js
npx ts-node src/index.ts # TypeScript ile
# --- Production ---
NODE_ENV=production node app.js
pm2 start app.js --name "express-api" -i max// package.json scripts
{
"scripts": {
"dev": "nodemon src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"pm2:start": "pm2 start ecosystem.config.js --env production"
}
}Next.js
# --- Development ---
npm run dev # localhost:3000
npx next dev -p 4000 # Farkli port
# --- Production ---
npm run build # .next klasoru olusur
npm run start # Production server
pm2 start npm --name "nextjs" -- start
# Standalone build (Docker icin ideal)
# next.config.js icinde output: 'standalone' ekle
node .next/standalone/server.js// next.config.js -- standalone ciktisi
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
images: {
domains: ["cdn.example.com"],
},
};
module.exports = nextConfig;Nuxt.js (Nuxt 3)
# --- Development ---
npm run dev # localhost:3000
npx nuxi dev --port 4000
# --- Production ---
npm run build # .output klasoru olusur
node .output/server/index.mjs # Node server olarak calistir
pm2 start .output/server/index.mjs --name "nuxt-app"
# Static site generation
npx nuxi generate
# dist/ klasorunu herhangi bir static hosting'e yukleReact (Vite)
# --- Development ---
npm run dev # localhost:5173
# --- Production ---
npm run build # dist/ klasoru olusur
# Statik dosyalari Nginx, Vercel, Netlify ile sun
# Preview (local production testi)
npm run preview# Nginx ile React SPA sunma
server {
listen 80;
server_name example.com;
root /var/www/react-app/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Vue.js (Vite)
# --- Development ---
npm run dev # localhost:5173
# --- Production ---
npm run build # dist/ klasoru olusur
# React ile ayni: statik dosyalari sun
npm run preview # Local production testiLaravel (PHP)
# --- Development ---
php artisan serve # localhost:8000
php artisan serve --port=9000
# --- Production ---
# Nginx + PHP-FPM kullan (asagidaki VPS kurulum bolumune bak)
# Production hazirlik komutlari
composer install --optimize-autoloader --no-dev
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan migrate --force
php artisan storage:link# Nginx Laravel yapilandirmasi
server {
listen 80;
server_name example.com;
root /var/www/laravel/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}Django (Python)
# --- Development ---
python manage.py runserver # localhost:8000
python manage.py runserver 0.0.0.0:9000
# --- Production ---
# Gunicorn + Nginx kullan
pip install gunicorn
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4
gunicorn myproject.wsgi:application \
--bind unix:/run/gunicorn.sock \
--workers 4 \
--timeout 120
# Production hazirlik
python manage.py collectstatic --noinput
python manage.py migrate --noinput# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon for Django
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/django-app
ExecStart=/var/www/django-app/venv/bin/gunicorn \
--access-logfile - \
--workers 4 \
--bind unix:/run/gunicorn.sock \
myproject.wsgi:application
[Install]
WantedBy=multi-user.targetFastAPI (Python)
# --- Development ---
uvicorn main:app --reload # localhost:8000
uvicorn main:app --reload --port 9000
# --- Production ---
pip install uvicorn[standard] gunicorn
# Uvicorn direkt
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
# Gunicorn + Uvicorn worker
gunicorn main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--access-logfile - \
--error-logfile -# /etc/systemd/system/fastapi.service
[Unit]
Description=FastAPI application
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/fastapi-app
Environment="PATH=/var/www/fastapi-app/venv/bin"
ExecStart=/var/www/fastapi-app/venv/bin/gunicorn main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind unix:/run/fastapi.sock
[Install]
WantedBy=multi-user.targetASP.NET Core (C#)
# --- Development ---
dotnet run # localhost:5000
dotnet watch run # Hot reload ile
# --- Production ---
dotnet publish -c Release -o ./publish
cd publish
dotnet MyApp.dll --urls "http://0.0.0.0:5000"
# Environment degiskeni ile
ASPNETCORE_ENVIRONMENT=Production dotnet MyApp.dll# /etc/systemd/system/aspnet-app.service
[Unit]
Description=ASP.NET Core Application
After=network.target
[Service]
User=www-data
WorkingDirectory=/var/www/aspnet-app/publish
ExecStart=/usr/bin/dotnet /var/www/aspnet-app/publish/MyApp.dll
Restart=always
RestartSec=10
SyslogIdentifier=aspnet-app
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.targetVPS Kurulum (VPS Setup: Ubuntu + Nginx + Application)
1. Sunucu İlk Kurulum (Initial Server Setup)
# Sistemi guncelle
sudo apt update && sudo apt upgrade -y
# Yeni kullanici olustur (root ile calisma)
sudo adduser deployer
sudo usermod -aG sudo deployer
# SSH key ile erisim ayarla
sudo mkdir -p /home/deployer/.ssh
sudo cp ~/.ssh/authorized_keys /home/deployer/.ssh/
sudo chown -R deployer:deployer /home/deployer/.ssh
sudo chmod 700 /home/deployer/.ssh
sudo chmod 600 /home/deployer/.ssh/authorized_keys2. Güvenlik Ayarları (Security Settings)
# SSH yapilandirmasi -- /etc/ssh/sshd_config
sudo nano /etc/ssh/sshd_config
# Asagidaki ayarlari degistir:
# PermitRootLogin no
# PasswordAuthentication no
# PubkeyAuthentication yes
# Port 2222 # Varsayilan portu degistir (opsiyonel)
sudo systemctl restart sshd
# Firewall (UFW)
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status3. Gerekli Yazılımların Kurulumu (Install Required Software)
# Node.js (NVM ile)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20
nvm alias default 20
# PM2
npm install -g pm2
# Nginx
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
# Certbot (SSL)
sudo apt install certbot python3-certbot-nginx -y
# Git
sudo apt install git -y
# Python (eger gerekiyorsa)
sudo apt install python3 python3-pip python3-venv -y
# PHP (eger gerekiyorsa)
sudo apt install php8.3-fpm php8.3-mysql php8.3-xml php8.3-curl php8.3-mbstring -y4. Nginx Yapılandırması (Nginx Configuration)
# /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com www.example.com;
# Node.js / Next.js / Express uygulamasi icin reverse proxy
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 90;
}
}# Site'i etkinlestir
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t # Yapilandirma testi
sudo systemctl reload nginx5. Uygulamayı Deploy Et (Deploy the Application)
# Proje klasoru olustur
sudo mkdir -p /var/www/myapp
sudo chown deployer:deployer /var/www/myapp
# Projeyi klonla
cd /var/www/myapp
git clone https://github.com/user/repo.git .
# Bagimliliklari kur
npm install --production
# .env dosyasini olustur
cp .env.example .env
nano .env
# Build (gerekiyorsa)
npm run build
# PM2 ile baslat
pm2 start ecosystem.config.js --env production
pm2 save
pm2 startup6. SSL Sertifikası (SSL Certificate - Let's Encrypt)
# SSL sertifikasi al
sudo certbot --nginx -d example.com -d www.example.com
# Otomatik yenileme testi
sudo certbot renew --dry-run
# Otomatik yenileme (cron zaten eklenir, kontrol et)
sudo systemctl status certbot.timerSSL sonrası Nginx otomatik olarak güncellenir:
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}Domain ve DNS Yönetimi (Domain & DNS Management)
DNS Kayıt Türleri (DNS Record Types)
| Kayıt Türü | Açıklama | Örnek |
|---|---|---|
| A | Domain'i IPv4 adresine yönlendirir | example.com -> 192.168.1.1 |
| AAAA | Domain'i IPv6 adresine yönlendirir | example.com -> 2001:db8::1 |
| CNAME | Domain'i başka bir domain'e yönlendirir | www -> example.com |
| MX | E-posta sunucusunu belirler | example.com -> mail.example.com |
| TXT | Metin kaydı (SPF, DKIM, doğrulama) | v=spf1 include:_spf.google.com ~all |
| NS | Nameserver belirler | example.com -> ns1.cloudflare.com |
Tipik DNS Yapılandırması (Typical DNS Configuration)
; A kayitlari
@ A 203.0.113.50 ; Ana domain -> sunucu IP
www CNAME example.com. ; www -> ana domain
; E-posta (Google Workspace ornegi)
@ MX 1 aspmx.l.google.com.
@ MX 5 alt1.aspmx.l.google.com.
@ MX 5 alt2.aspmx.l.google.com.
@ TXT "v=spf1 include:_spf.google.com ~all"
; Alt domain'ler
api A 203.0.113.50 ; api.example.com
staging A 203.0.113.51 ; staging.example.comCloudflare Ayarları (Cloudflare Settings)
Önerilen Cloudflare ayarları:
1. DNS Proxy (turuncu bulut) -> A ve CNAME kayıtları için aktif
2. SSL/TLS -> Full (Strict) mod
3. Always Use HTTPS -> Açık
4. Auto Minify -> HTML, CSS, JS
5. Brotli -> Açık
6. HTTP/2 ve HTTP/3 -> Açık
7. Security Level -> Medium
8. Bot Fight Mode -> Açık
Page Rules (örnek):
- *.example.com/wp-admin/* -> Security Level: High
- example.com/api/* -> Cache Level: Bypass
- example.com/static/* -> Cache Level: Cache Everything, Edge TTL: 1 monthCloudflare ile Orijin IP Gizleme (Origin IP Protection with Cloudflare)
# Nginx'te gercek IP'yi almak icin
# /etc/nginx/conf.d/cloudflare.conf
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
real_ip_header CF-Connecting-IP;CI/CD -- GitHub Actions ile Deploy (CI/CD with GitHub Actions)
Basit VPS Deploy (Simple VPS Deploy via SSH)
# .github/workflows/deploy.yml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run tests
run: npm test
- name: Deploy to VPS
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm ci --production
npm run build
pm2 reload ecosystem.config.js --env productionDocker ile Deploy (Deploy with Docker)
# .github/workflows/docker-deploy.yml
name: Docker Deploy
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker stop myapp || true
docker rm myapp || true
docker run -d \
--name myapp \
--restart unless-stopped \
-p 3000:3000 \
--env-file /var/www/myapp/.env \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latestVercel Preview Deploy
# .github/workflows/preview.yml
name: Vercel Preview
on:
pull_request:
branches: [main]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Vercel CLI
run: npm i -g vercel
- name: Deploy Preview
run: |
vercel pull --yes --token=${{ secrets.VERCEL_TOKEN }}
vercel build --token=${{ secrets.VERCEL_TOKEN }}
PREVIEW_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
echo "Preview URL: $PREVIEW_URL"
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Preview deployed: ${process.env.PREVIEW_URL}`
})GitHub Actions Secrets Ayarlama (Setting Up GitHub Actions Secrets)
# GitHub CLI ile secret ekle
gh secret set VPS_HOST --body "203.0.113.50"
gh secret set VPS_USER --body "deployer"
gh secret set VPS_SSH_KEY < ~/.ssh/id_rsa
# Secret listele
gh secret listProduction Checklist (Production Hazırlık Listesi)
Ortam Değişkenleri (Environment Variables)
# .env dosyasi ASLA repoya eklenmemeli
# .gitignore icinde .env bulunmali
# .env.example -- sablonu repoda tut
NODE_ENV=production
PORT=3000
DATABASE_URL=
REDIS_URL=
JWT_SECRET=
API_KEY=
# Ortam degiskenlerini dogrula (baslangicta)// config/env.js -- baslangicta dogrulama
const requiredEnvVars = [
"NODE_ENV",
"PORT",
"DATABASE_URL",
"JWT_SECRET",
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`HATA: ${envVar} ortam degiskeni tanimli degil`);
process.exit(1);
}
}SSL/TLS Kontrol (SSL/TLS Check)
# SSL sertifika durumunu kontrol et
sudo certbot certificates
# SSL test (harici)
# https://www.ssllabs.com/ssltest/
# Nginx SSL ayarlari -- guclu yapilandirma
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS header
add_header Strict-Transport-Security "max-age=63072000" always;Monitoring ve Alerting (İzleme ve Uyarılar)
# PM2 monitoring
pm2 install pm2-server-monit
# Disk kullanimi izle
df -h
# Bellek kullanimi
free -m
# Sistem yukleri
uptime
htop
# Nginx erisim ve hata loglari
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.logHarici monitoring servisleri:
| Servis | Tür | Ücretsiz Plan |
|---|---|---|
| UptimeRobot | Uptime monitoring | 50 monitor |
| Better Stack | Log + uptime | Sınırlı |
| Sentry | Error tracking | 5K event/ay |
| Grafana Cloud | Metrik + log | 10K metrik |
| Datadog | Tam monitoring | Sınırlı |
Backup Stratejisi (Backup Strategy)
# Veritabani yedegi (PostgreSQL)
pg_dump -U postgres dbname > backup_$(date +%Y%m%d_%H%M%S).sql
# Veritabani yedegi (MySQL)
mysqldump -u root -p dbname > backup_$(date +%Y%m%d_%H%M%S).sql
# Otomatik yedek script'i
#!/bin/bash
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="myapp_production"
# PostgreSQL yedek
pg_dump -U postgres $DB_NAME | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"
# Dosya yedegi
tar -czf "$BACKUP_DIR/files_$DATE.tar.gz" /var/www/myapp/uploads/
## 30 gunden eski yedekleri sil
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
echo "Yedekleme tamamlandi: $DATE"# Cron ile otomatik yedekleme (her gun gece 3'te)
crontab -e
## 0 3 * * * /home/deployer/scripts/backup.sh >> /var/log/backup.log 2>&1Graceful Shutdown (Zarif Kapanış)
// Node.js graceful shutdown ornegi
const server = app.listen(PORT, () => {
console.log(`Server ${PORT} portunda calisiyor`);
});
// Graceful shutdown
const gracefulShutdown = (signal) => {
console.log(`${signal} sinyali alindi. Sunucu kapatiliyor...`);
server.close(() => {
console.log("HTTP sunucusu kapatildi");
// Veritabani baglantisini kapat
mongoose.connection.close(false, () => {
console.log("MongoDB baglantisi kapatildi");
process.exit(0);
});
});
// 10 saniye sonra zorla kapat
setTimeout(() => {
console.error("Zorla kapatiliyor...");
process.exit(1);
}, 10000);
};
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));Güvenlik Header'ları (Security Headers)
# /etc/nginx/conf.d/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;Rate Limiting (İstek Hız Sınırlama - Nginx)
# /etc/nginx/conf.d/rate-limit.conf
# Genel limit: saniyede 10 istek
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# API limit: saniyede 30 istek
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
# Login limit: dakikada 5 istek
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
server {
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://localhost:3000;
}
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://localhost:3000;
}
location /api/auth/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://localhost:3000;
}
}Faydalı İpuçları (Useful Tips)
Deploy Öncesi Kontrol Listesi (Pre-Deploy Checklist)
[ ] .env dosyası production değerleri ile dolu
[ ] .gitignore içinde .env, node_modules, dist var
[ ] npm audit ile güvenlik açıkları kontrol edildi
[ ] Build hatasız tamamlanıyor
[ ] Testler geçti
[ ] Veritabanı migration'ları hazırlandı
[ ] SSL sertifikası geçerli
[ ] Firewall kuralları ayarlandı
[ ] Backup scripti çalışır durumda
[ ] Monitoring aktif
[ ] Graceful shutdown implement edildi
[ ] Error tracking (Sentry vb.) kurulu
[ ] Log rotation ayarlandı
[ ] Rate limiting aktifSık Kullanılan Komutlar (Frequently Used Commands)
# Sunucu durumu
sudo systemctl status nginx
pm2 status
# Disk alani
df -h
du -sh /var/www/*
# Bellek durumu
free -m
# Aktif baglantilar
ss -tlnp
# Port kontrol
sudo lsof -i :3000
# Nginx log analizi
sudo cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
# SSL sertifika bitis tarihi
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# DNS kontrol
dig example.com A +short
dig example.com MX +short
nslookup example.comZero-Downtime Deploy Stratejisi (Zero-Downtime Deploy Strategy)
# PM2 ile zero-downtime reload
pm2 reload ecosystem.config.js --env production
# Blue-Green deploy (basit versiyon)
#!/bin/bash
APP_DIR="/var/www/myapp"
RELEASE_DIR="/var/www/releases/$(date +%Y%m%d_%H%M%S)"
# Yeni release olustur
mkdir -p $RELEASE_DIR
cd $RELEASE_DIR
git clone https://github.com/user/repo.git .
npm ci --production
npm run build
# Symlink degistir
ln -sfn $RELEASE_DIR $APP_DIR/current
# PM2 reload
cd $APP_DIR/current
pm2 reload ecosystem.config.js --env production
# Eski release'leri temizle (son 5'i tut)
ls -dt /var/www/releases/*/ | tail -n +6 | xargs rm -rf
echo "Deploy tamamlandi: $RELEASE_DIR"Rollback (Geri Alma)
# Git ile rollback
cd /var/www/myapp
git log --oneline -10 # Son commitleri gor
git checkout <commit-hash>
npm ci --production
npm run build
pm2 reload ecosystem.config.js --env production
# Symlink ile rollback (blue-green kullaniyorsan)
ls /var/www/releases/ # Mevcut release'leri listele
ln -sfn /var/www/releases/20260409_120000 /var/www/myapp/current
pm2 reload ecosystem.config.js --env productionİlgili Rehberler (Related Guides)
DevOps & Tools (DevOps ve Araçlar)
- DevOps Genel Bakış
- Git Notları
- Docker Rehberi
- Kubernetes
- Nginx Rehberi
- Linux CLI
- Ubuntu Rehberi
- VS Code Rehberi
- Regex Rehberi