İçindekiler
- 1 GitHub Actions ile VPS Deploy Neden Bu Kadar Kritik Hale Geldi?
- 2 Genel Mimari: GitHub Actions → VPS Akışı Nasıl Kurulur?
- 3 VPS Tarafı Hazırlığı: Dizin Yapısı, systemd ve Nginx/PHP-FPM
- 4 GitHub Actions Temelleri: Workflow, Secrets ve Ortamlar
- 5 PHP (Laravel/Symfony) İçin Örnek GitHub Actions Workflow
- 6 Node.js İçin Zero‑Downtime GitHub Actions Workflow
- 7 Rollback, Blue‑Green ve Canary: İşler Sarpa Sarınca Ne Yapacağız?
- 8 CI/CD Sürecini Destekleyen Ek Adımlar: Git, Load Test ve İzleme
- 9 DCHost Üzerinde Örnek Senaryo: PHP API + Node.js Frontend
- 10 Sonuç ve Sonraki Adımlar: Küçük Bir YAML, Büyük Bir Rahatlık
GitHub Actions ile VPS Deploy Neden Bu Kadar Kritik Hale Geldi?
Geliştirme ekibinizin haftalık planlama toplantısında, konuşulan konular büyük ihtimalle şunlar: “Hangi özelliği yetiştireceğiz?”, “Testler neden yavaş?”, “Canlıda kesinti olmadan nasıl güncelleme yaparız?”. Kod yazmak artık işin sadece bir kısmı. Asıl değer, o kodun hızlı, güvenli ve kesinti olmadan canlı ortama ulaşmasında ortaya çıkıyor. İşte tam bu noktada GitHub Actions ile VPS’e otomatik deploy, özellikle PHP ve Node.js projeleri için oyunu tamamen değiştiriyor.
Manuel FTP ile dosya atmak, uzak masaüstü ile sunucuya bağlanıp klasör kopyalamak, “yanlış dosyayı sildim” paniği yaşamak istemiyorsanız; sürüm kontrollü bir CI/CD hattı kurmak artık bir lüks değil, zorunluluk. DCHost olarak biz de kendi iç projelerimizde ve müşterilerimizin altyapılarında GitHub Actions → VPS akışını standart hale getiriyoruz. Bu yazıda, PHP ve Node.js uygulamalarını zero‑downtime (sıfıra yakın kesinti) ile nasıl yayınlayabileceğinizi, adım adım ve pratik örneklerle anlatacağım.
Odak noktamız şu olacak: GitHub Actions içinde build ve test işlemlerini yapmak, çıktıyı DCHost üzerindeki VPS’inize göndermek, sunucuda sembolik sürümler (releases + current symlink) ile atomik geçiş sağlamak ve gerektiğinde tek komutla rollback yapabilmek.
Genel Mimari: GitHub Actions → VPS Akışı Nasıl Kurulur?
Önce büyük resmi netleştirelim. Sağlıklı bir CI/CD hattında tipik akış şöyle görünür:
- Geliştirici, GitHub deposunda main veya production branşına kod gönderir (push veya pull request merge).
- GitHub Actions workflow’u tetiklenir: testler, lint, build, statik analiz çalışır.
- Build başarılıysa, workflow VPS’e güvenli SSH bağlantısı kurar.
- VPS üzerinde yeni bir
releaseklasörü oluşturulur, dosyalar buraya kopyalanır. - Gerekli migrate, cache clear, build komutları sunucu tarafında çalışır.
currentsembolik linki, eski sürümden yeni sürüme atomik olarak kaydırılır.- PHP-FPM, Node.js servisi veya reverse proxy’ye hafif bir reload verilir.
Bu yaklaşımı, VPS’e sıfır kesinti CI/CD kurulumunu rsync ve sembolik sürümlerle anlattığımız rehberde genel hatlarıyla ele almıştık. Bu yazıda ise özellikle GitHub Actions YAML tarafını, PHP ve Node.js örnekleriyle detaylandırıyoruz.
Branch Stratejisi ve Ortamlar
Basit ama iş gören bir kurgu şöyle olabilir:
develop: Geliştirme ortamına (staging) otomatik deploy.mainveyamaster: Canlı ortama otomatik veya onaylı deploy.
GitHub Actions tarafında environments kullanarak “staging” ve “production” ortamlarını tanımlayabilir, production deploy’larını manuel onaya bağlayabilirsiniz.
Güvenlik Perspektifi: SSH Anahtarı ve Deploy Kullanıcısı
VPS’e doğrudan root ile bağlanmak yerine, sadece deploy işlerine özel, yetkisi sınırlı bir kullanıcı kullanmak en sağlıklısıdır. Bunu, VPS sunucu güvenliğini pratik şekilde anlattığımız yazıda geniş biçimde işlemiştik. Özetle:
- DCHost VPS’inizde
deployisminde yeni bir kullanıcı açın. - Bu kullanıcıyı sadece ilgili proje dizinine yazma yetkisi olacak şekilde kısıtlayın.
- SSH erişimini sadece anahtar tabanlı kimlik doğrulama ile sınırlandırın.
VPS Tarafı Hazırlığı: Dizin Yapısı, systemd ve Nginx/PHP-FPM
GitHub Actions ile otomatik deploy’un rahat etmesi için, sunucu tarafını bir kez düzgün kurmanız yeterli. Sonrası küçük iyileştirmeler.
Standart Dizin Yapısı
Örnek bir proje yolu üzerinden gidelim:
/var/www/myapp
├── releases
│ ├── 20250101010101
│ ├── 20250102123045
│ └── ...
├── shared
│ ├── .env
│ ├── storage (PHP için)
│ └── uploads (Node.js statik içerikleri vs.)
└── current -> /var/www/myapp/releases/20250102123045
Zero‑downtime’ın kalbi burası. current sembolik linki her yeni sürümde başka bir klasöre işaret edecek. Nginx, PHP-FPM veya Node.js servisiniz hep /var/www/myapp/current yolunu görecek; siz arka planda releases klasöründe yeni sürümü hazırlayıp, en son symlink’i saniyeler içinde kaydıracaksınız.
PHP Uygulaması İçin Örnek Nginx Konfigürasyonu
Laravel veya başka bir modern PHP framework’ü için basit bir Nginx konfigürasyonu şöyle olabilir:
server {
server_name example.com;
root /var/www/myapp/current/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
}
Burada kritik nokta: root her zaman current/public’i işaret ediyor. Deploy sırasında releases klasöründe ne yaparsanız yapın, Nginx tarafı bunu fark etmeden yoluna devam ediyor.
Node.js Uygulaması İçin systemd Servisi
Node.js projelerinde süreç yönetimi için systemd veya PM2 kullanabilirsiniz. PM2 tarafını Node.js’i canlıya alırken panik yapmama rehberimizde detaylı anlattık. Burada sade bir systemd örneği verelim:
[Unit]
Description=MyApp Node.js Service
After=network.target
[Service]
User=deploy
Group=deploy
WorkingDirectory=/var/www/myapp/current
ExecStart=/usr/bin/node dist/server.js
Restart=always
Environment=NODE_ENV=production
Environment=PORT=3000
[Install]
WantedBy=multi-user.target
Zero‑downtime için, yeni sürümü releases altında hazırlayıp current symlink’ini kaydırdıktan sonra systemctl reload-or-restart myapp kullanacağız. Doğru kurgulandığında, kesinti süresi milisaniyelerle sınırlı kalır.
GitHub Actions Temelleri: Workflow, Secrets ve Ortamlar
Şimdi GitHub tarafını toparlayalım. Bir workflow dosyası tipik olarak .github/workflows/deploy.yml altında durur ve aşağıdaki bileşenlerden oluşur:
- on: Hangi olaylarda tetikleneceği (push, pull_request, manual dispatch vb.).
- jobs: build, test, deploy gibi işleri tanımlayan bloklar.
- runs-on: Workflow’un koşacağı runner (genellikle GitHub’ın hosted runner’ları).
- secrets: VPS bilgileri, SSH anahtarları, veritabanı şifreleri.
Gerekli Secrets Örnekleri
GitHub deposu ayarlarından şu secrets değerlerini tanımlamanızı öneririm:
VPS_HOST– DCHost VPS IP adresiniz veya hostname.VPS_USER– Örneğindeploykullanıcısı.SSH_PRIVATE_KEY– Deploy için kullanılacak özel SSH anahtarı.APP_PATH– Örneğin/var/www/myapp.
Daha ileri seviye gizli yönetimini, VPS’te secrets yönetimini sops + age ile anlattığımız yazıda detaylı inceledik. GitHub Actions tarafında da benzer prensipler geçerli: gizlileri repoya asla düz metin koymuyor, her şeyi secrets üzerinden besliyoruz.
PHP (Laravel/Symfony) İçin Örnek GitHub Actions Workflow
Şimdi somut bir örnek üzerinden gidelim. Laravel tabanlı bir PHP uygulamasını DCHost VPS’e otomatik ve neredeyse kesintisiz deploy eden basit ama iş gören bir YAML yazalım.
1. Build ve Test Aşaması
name: Deploy PHP App to VPS
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Kodu çek
uses: actions/checkout@v4
- name: PHP kur
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, intl, pdo_mysql
- name: Composer cache
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
- name: Composer install
run: composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
- name: Testleri çalıştır
run: |
php artisan test || vendor/bin/phpunit || echo "Test komutu uyarlanmalı"
Buraya kadar olan kısım tamamen GitHub runner üzerinde gerçekleşiyor. Canlı sunucuya dokunmadan önce kodun derlenip test edilmesini sağlıyoruz.
2. Build Çıktısını Paketleyip VPS’e Göndermek
Şimdi sırada artefact’i (uygulama çıktısını) VPS’e aktarmak ve orada yeni bir sürüm klasörü oluşturmak var.
- name: SSH anahtarını ayarla
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts
- name: Release klasörü adını oluştur
id: vars
run: echo "RELEASE_DIR=$(date +%Y%m%d%H%M%S)" >> $GITHUB_OUTPUT
- name: Projeyi arşivle
run: |
tar czf release.tgz .
- name: Arşivi VPS'e gönder
run: |
scp release.tgz ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:/tmp/release_${{ steps.vars.outputs.RELEASE_DIR }}.tgz
3. Sunucu Üzerinde Yeni Sürümü Hazırlamak
Şimdi en kritik kısımdayız. SSH ile VPS’e bağlanıp yeni release klasörünü oluşturacağız.
- name: VPS üzerinde release'i hazırla
run: |
ssh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << 'EOF'
set -e
APP_PATH="${{ secrets.APP_PATH }}"
RELEASE_DIR="${{ steps.vars.outputs.RELEASE_DIR }}"
mkdir -p "$APP_PATH/releases/$RELEASE_DIR"
tar xzf "/tmp/release_${RELEASE_DIR}.tgz" -C "$APP_PATH/releases/$RELEASE_DIR"
rm "/tmp/release_${RELEASE_DIR}.tgz"
# shared .env ve storage'i bağla
ln -nfs "$APP_PATH/shared/.env" "$APP_PATH/releases/$RELEASE_DIR/.env"
mkdir -p "$APP_PATH/shared/storage"
rm -rf "$APP_PATH/releases/$RELEASE_DIR/storage"
ln -nfs "$APP_PATH/shared/storage" "$APP_PATH/releases/$RELEASE_DIR/storage"
cd "$APP_PATH/releases/$RELEASE_DIR"
composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
php artisan config:cache || true
php artisan route:cache || true
php artisan view:cache || true
php artisan migrate --force || true
# current symlink'ini atomik olarak güncelle
ln -nfs "$APP_PATH/releases/$RELEASE_DIR" "$APP_PATH/current_new"
mv -fT "$APP_PATH/current_new" "$APP_PATH/current"
# PHP-FPM reload
sudo systemctl reload php8.2-fpm || sudo systemctl reload php-fpm
# Eski release'leri temizle (son 5'i bırak)
cd "$APP_PATH/releases"
ls -1t | tail -n +6 | xargs -r rm -rf
EOF
Burada ln -nfs ve mv -fT kullanımı sayesinde, current symlink’inin güncellenmesi atomik gerçekleşiyor. Yani Nginx/PHP-FPM, bir anda eski sürümden yeni sürüme geçiyor; arada bozuk bir durum görmüyor. Veritabanı migration’larını da deploy sürecine entegre ettiğiniz için, kod ve şema birlikte güncelleniyor.
Node.js İçin Zero‑Downtime GitHub Actions Workflow
Benzer mantığı Node.js uygulamaları için de kurabiliriz. Örneğin Nest.js veya Express tabanlı bir API’niz olsun; build sürecini GitHub Actions’ta yapıp sadece derlenmiş çıktıyı VPS’e göndererek sunucu yükünü azaltabilirsiniz.
1. Build ve Test
name: Deploy Node App to VPS
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Kodu çek
uses: actions/checkout@v4
- name: Node versiyonunu ayarla
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Bağımlılıkları yükle
run: |
npm ci
- name: Testleri çalıştır
run: |
npm test || echo "Test komutu proje yapısına göre uyarlanmalı"
- name: Prod build al
run: |
npm run build
2. Build Çıktısını Gönderme ve Servisi Yenileme
Burada genellikle dist veya .next gibi derlenmiş klasörü, bir release klasörü içine kopyalayıp symlink’i kaydırıyoruz.
- name: SSH anahtarını ayarla
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts
- name: Release klasörü adını oluştur
id: vars
run: echo "RELEASE_DIR=$(date +%Y%m%d%H%M%S)" >> $GITHUB_OUTPUT
- name: Sadece gerekli dosyaları arşivle
run: |
tar czf node_release.tgz package.json package-lock.json dist
- name: Arşivi VPS'e gönder
run: |
scp node_release.tgz ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:/tmp/node_release_${{ steps.vars.outputs.RELEASE_DIR }}.tgz
- name: VPS üzerinde Node release'ini hazırla
run: |
ssh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << 'EOF'
set -e
APP_PATH="${{ secrets.APP_PATH }}"
RELEASE_DIR="${{ steps.vars.outputs.RELEASE_DIR }}"
mkdir -p "$APP_PATH/releases/$RELEASE_DIR"
tar xzf "/tmp/node_release_${RELEASE_DIR}.tgz" -C "$APP_PATH/releases/$RELEASE_DIR"
rm "/tmp/node_release_${RELEASE_DIR}.tgz"
# shared .env'i bağla
ln -nfs "$APP_PATH/shared/.env" "$APP_PATH/releases/$RELEASE_DIR}/.env"
cd "$APP_PATH/releases/$RELEASE_DIR"
npm ci --omit=dev
# symlink'i atomik güncelle
ln -nfs "$APP_PATH/releases/$RELEASE_DIR" "$APP_PATH/current_new"
mv -fT "$APP_PATH/current_new" "$APP_PATH/current"
# Node servisini reload et
sudo systemctl reload myapp || sudo systemctl restart myapp
# Eski release'leri temizle
cd "$APP_PATH/releases"
ls -1t | tail -n +6 | xargs -r rm -rf
EOF
Eğer PM2 kullanıyorsanız, systemctl reload yerine pm2 reload myapp benzeri bir komutla zero‑downtime restart kurgulayabilirsiniz. Bunun ayrıntılarını, PM2 ve systemd ile Node.js’i sıfır kesintiyle yayınlama rehberimizde adım adım anlattık.
Rollback, Blue‑Green ve Canary: İşler Sarpa Sarınca Ne Yapacağız?
Hiçbir deploy süreci %100 hatasız değildir. Önemli olan, bir problem çıktığında ne kadar hızlı geri dönebileceğinizdir. Sembolik current stratejisi bu yüzden çok güçlü.
Basit Rollback Stratejisi
Sunucuda şu komutla son 5 release’i görebilirsiniz:
cd /var/www/myapp/releases
ls -1t
Örneğin en yeni sürüm hatalıysa, bir önceki klasöre dönmek için:
PREV=$(ls -1t | sed -n '2p')
ln -nfs "/var/www/myapp/releases/$PREV" "/var/www/myapp/current_new"
mv -fT "/var/www/myapp/current_new" "/var/www/myapp/current"
# PHP veya Node servisini reload et
sudo systemctl reload php8.2-fpm || sudo systemctl reload myapp
Daha gelişmiş senaryolarda, Nginx ağırlıklı yönlendirme ve sağlık kontrolü ile canary dağıtım yapmak da mümkün. Bu konsepte ilgi duyuyorsanız, VPS’te canary dağıtımını Nginx ve health check ile anlattığımız rehbere göz atabilirsiniz.
CI/CD Sürecini Destekleyen Ek Adımlar: Git, Load Test ve İzleme
Otomatik deploy sadece “kod gitsin” demek değildir; ekosistemi bütün olarak düşünmek gerekir.
Git ile Otomatik Deploy Kültürü
Ekipler genellikle önce Git ile otomatik deploy’un temellerini öğreniyor, ardından bunu GitHub Actions gibi CI/CD sistemleriyle pekiştiriyor. Önemli olan; canlı ortama erişimi, doğrudan SSH açmak yerine pull request + merge + pipeline üçlüsüne bağlamak.
Yük Testi ve Kapasite Analizi
Yeni bir özellik deploy ettiğinizde, sadece “çalışıyor mu” değil, “yük altında nasıl davranıyor” sorusunu da yanıtlamanız gerekir. Bunu, k6, JMeter ve Locust ile load test yapmayı anlattığımız rehberde detaylı işledik. CI/CD hattınıza basit yük testlerini entegre ederek, problemli performans değişikliklerini canlıya çıkmadan yakalayabilirsiniz.
DCHost Üzerinde Örnek Senaryo: PHP API + Node.js Frontend
Gerçek bir projeden kolayca türetilebilecek bir senaryo üzerinden düşünelim. DCHost üzerinde 4 vCPU, 8 GB RAM ve NVMe diskli bir VPS’iniz var. Aynı sunucuda:
- Laravel tabanlı bir API (PHP 8.2, Nginx + PHP-FPM),
- Next.js tabanlı bir frontend (Node 20, Nginx reverse proxy),
- Redis ve MariaDB gibi ek servisler
çalışıyor olsun.
Bu projeyi şu şekilde kurgulayabilirsiniz:
- Tek GitHub monorepo;
api/vefrontend/klasörleri. - İki ayrı GitHub Actions workflow:
deploy-api.ymlvedeploy-frontend.yml. - VPS üzerinde iki ayrı
APP_PATH:/var/www/apive/var/www/frontend. - Nginx siteleri, ilgili
currentklasörlerine bakacak şekilde ayarlı.
Her push’ta:
api/altındaki değişiklikler varsa PHP workflow’u tetiklenir ve sadece API deploy edilir.frontend/altındaki değişiklikler varsa Node workflow’u tetiklenir ve sadece frontend deploy edilir.
Böylece tek VPS üzerinde hem PHP hem de Node.js dünyasını, GitHub Actions ile disiplinli ve zero‑downtime’a çok yakın şekilde yönetmiş olursunuz. Trafik ve kaynak ihtiyacınız arttığında, aynı mimariyi DCHost üzerinde ek VPS’ler veya dedicated sunucular ile yatayda genişletebilirsiniz.
Sonuç ve Sonraki Adımlar: Küçük Bir YAML, Büyük Bir Rahatlık
Manuel deploy süreçleri, kısa vadede “kolaymış” gibi hissettirse de, orta vadede hataya açık, takip edilemez ve ekip büyüdükçe sürdürülemez hale geliyor. GitHub Actions ile VPS’e otomatik deploy kurduğunuzda:
- Her değişiklik, testlerden geçmiş ve kayıt altına alınmış şekilde canlıya gider.
- Zero‑downtime’a yakın yayın sayesinde kullanıcılarınız deploy anlarını fark etmez.
- Rollback, birkaç komutla veya küçük bir script ile saniyeler içinde mümkün hale gelir.
- Sunucu tarafında “kim, ne zaman, ne kopyaladı” sorusu tarihe karışır.
DCHost ekibi olarak, ister PHP ister Node.js olsun, projelerinizi GitHub Actions ile otomatik deploy edecek şekilde kurgulamanıza yardımcı olacak altyapıyı ve tecrübeyi sunuyoruz. İhtiyacınıza göre tek VPS, çoklu VPS veya dedicated sunucu mimarilerini birlikte planlayabilir; CI/CD hattınızı güvenlik, performans ve yedeklilik kriterleriyle birlikte tasarlayabiliriz.
Elinizdeki projeyi GitHub Actions ile otomatik deploy’a taşımak istiyorsanız, küçükten başlayın: Önce staging ortamı için basit bir workflow kurun, ardından zero‑downtime ve rollback adımlarını ekleyin. Bu rehberdeki örnekleri kendi projenize uyarlayın, ihtiyaç duyduğunuz noktada da DCHost ekibiyle iletişime geçip altyapı tarafını birlikte netleştirelim.
