Skip to content

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)

PlatformTürÜcretsiz PlanÖzel DomainSSLAuto DeployÖlçeklemeEn Uygun
VercelServerlessVarVarVarVarOtomatikNext.js, React, statik siteler
NetlifyServerlessVarVarVarVarOtomatikStatik siteler, JAMstack
RailwayPaaSSınırlıVarVarVarManuelFull-stack, veritabanı ile
RenderPaaSSınırlıVarVarVarManuel/OtoWeb servisleri, cron işler
VPS (Hetzner/DigitalOcean)IaaSYokManuelManuelManuelManuelTam kontrol, özel yapılar
AWS (EC2/ECS/Lambda)IaaS/PaaSSınırlıVarVarVarOtomatikBüyük ölçekli, kurumsal

Platform Seçim Kriterleri (Platform Selection Criteria)

KriterVercel/NetlifyRailway/RenderVPSAWS
Maliyet (düşük trafik)ÜcretsizDüşükAylık sabitKullanıma göre
Maliyet (yüksek trafik)PahalıOrtaSabitDeğişken
Öğrenme eğrisiKolayKolayOrtaZor
Kontrol seviyesiDüşükOrtaTamTam
VeritabanıHariciDahiliKendin kurManaged servis
SSH erişimiYokSınırlıTamTam
Bakım gerekliliğiYokDüşükYüksekOrta-Yüksek

Vercel

bash
# Vercel CLI kurulumu
npm i -g vercel

# Projeyi deploy et
vercel

# Production deploy
vercel --prod

# Ortam degiskeni ekle
vercel env add VARIABLE_NAME production

vercel.json yapılandırması:

json
{
  "version": 2,
  "builds": [
    { "src": "package.json", "use": "@vercel/next" }
  ],
  "routes": [
    { "src": "/api/(.*)", "dest": "/api/$1" },
    { "src": "/(.*)", "dest": "/$1" }
  ],
  "env": {
    "DATABASE_URL": "@database-url"
  }
}

Netlify

bash
# Netlify CLI kurulumu
npm i -g netlify-cli

# Giris yap
netlify login

# Projeyi baslat
netlify init

# Deploy
netlify deploy --prod --dir=dist

netlify.toml yapılandırması:

toml
[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 = 200

Railway

bash
# 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 logs

PM2 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)

bash
# 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 all

Durum ve İzleme (Status & Monitoring)

bash
# 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 plus

Log Yönetimi (Log Management)

bash
# 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-ss

Ecosystem Dosyası (Multi-App Configuration)

ecosystem.config.js -- birden fazla uygulamayı tek dosyayla yönetme:

js
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,
      },
    },
  ],
};
bash
# 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.js

Startup ve Save (Startup & Persistence)

bash
# 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 unstartup

PM2 vs Diğer Process Manager'lar (PM2 vs Other Process Managers)

ÖzellikPM2Supervisorsystemd
Dil desteğiNode.js (öncelikli), Python, GoHerhangi bir dilHerhangi bir dil
Cluster modeVar (dahili)YokYok
Zero-downtime reloadVarYokSınırlı
Log yönetimiDahili + rotateDahilijournalctl
Monitoringpm2 monit, pm2 plussupervisorctlsystemctl status
Watch & restartVarYokYok
Ecosystem dosyasıVarini dosyasıunit dosyası
Startup integrationpm2 startupOS'e göreDahili
Web arayüzüpm2 plus (bulut)supervisord webcockpit
Kaynak tüketimiOrtaDüşükÇok düşük
Kurulum kolaylığınpm installapt installDahili
Önerilen kullanımNode.js projeleriPython/GenelSistem servisleri

Sunucu Proje Başlatma Komutları (Server Project Startup Commands)

Her framework için geliştirme (dev) ve production başlatma komutları.

Express.js

bash
# --- 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
js
// 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

bash
# --- 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
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)

bash
# --- 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 yukle

React (Vite)

bash
# --- 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
# 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)

bash
# --- Development ---
npm run dev                         # localhost:5173

# --- Production ---
npm run build                       # dist/ klasoru olusur
# React ile ayni: statik dosyalari sun

npm run preview                     # Local production testi

Laravel (PHP)

bash
# --- 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
# 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)

bash
# --- 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
ini
# /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.target

FastAPI (Python)

bash
# --- 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 -
ini
# /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.target

ASP.NET Core (C#)

bash
# --- 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
ini
# /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.target

VPS Kurulum (VPS Setup: Ubuntu + Nginx + Application)

1. Sunucu İlk Kurulum (Initial Server Setup)

bash
# 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_keys

2. Güvenlik Ayarları (Security Settings)

bash
# 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 status

3. Gerekli Yazılımların Kurulumu (Install Required Software)

bash
# 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 -y

4. Nginx Yapılandırması (Nginx Configuration)

nginx
# /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;
    }
}
bash
# Site'i etkinlestir
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t                       # Yapilandirma testi
sudo systemctl reload nginx

5. Uygulamayı Deploy Et (Deploy the Application)

bash
# 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 startup

6. SSL Sertifikası (SSL Certificate - Let's Encrypt)

bash
# 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.timer

SSL sonrası Nginx otomatik olarak güncellenir:

nginx
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
ADomain'i IPv4 adresine yönlendirirexample.com -> 192.168.1.1
AAAADomain'i IPv6 adresine yönlendirirexample.com -> 2001:db8::1
CNAMEDomain'i başka bir domain'e yönlendirirwww -> example.com
MXE-posta sunucusunu belirlerexample.com -> mail.example.com
TXTMetin kaydı (SPF, DKIM, doğrulama)v=spf1 include:_spf.google.com ~all
NSNameserver belirlerexample.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.com

Cloudflare 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 month

Cloudflare ile Orijin IP Gizleme (Origin IP Protection with Cloudflare)

nginx
# 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)

yaml
# .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 production

Docker ile Deploy (Deploy with Docker)

yaml
# .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 }}:latest

Vercel Preview Deploy

yaml
# .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)

bash
# 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 list

Production Checklist (Production Hazırlık Listesi)

Ortam Değişkenleri (Environment Variables)

bash
# .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)
js
// 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)

bash
# 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)

bash
# 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.log

Harici monitoring servisleri:

ServisTürÜcretsiz Plan
UptimeRobotUptime monitoring50 monitor
Better StackLog + uptimeSınırlı
SentryError tracking5K event/ay
Grafana CloudMetrik + log10K metrik
DatadogTam monitoringSınırlı

Backup Stratejisi (Backup Strategy)

bash
# 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"
bash
# Cron ile otomatik yedekleme (her gun gece 3'te)
crontab -e
## 0 3 * * * /home/deployer/scripts/backup.sh >> /var/log/backup.log 2>&1

Graceful Shutdown (Zarif Kapanış)

js
// 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)

nginx
# /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)

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 aktif

Sık Kullanılan Komutlar (Frequently Used Commands)

bash
# 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.com

Zero-Downtime Deploy Stratejisi (Zero-Downtime Deploy Strategy)

bash
# 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)

bash
# 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

DevOps & Tools (DevOps ve Araçlar)

Diğer Kategoriler (Other Categories)

Developer Guides & Technical References