GitHub Actions: deployment na VPS — pełny workflow SSH
Opublikowano: 9 kwietnia 2026 · Kategoria: DevOps
GitHub Actions to platforma CI/CD zintegrowana bezpośrednio z repozytoriami GitHub. Pozwala automatyzować testy, budowanie i deployment bez konieczności utrzymywania zewnętrznego Jenkinsa czy GitLab CI. W tym artykule pokażemy kompletny workflow deploymentu aplikacji Node.js na VPS przez SSH, z użyciem secrets, matrix builds i cachingu — gotowy do skopiowania i dostosowania do twojego projektu.
Struktura katalogu .github/workflows
GitHub Actions szuka plików YAML w katalogu .github/workflows/ w głównym katalogu
repozytorium. Każdy plik YAML to osobny workflow, który może mieć wiele jobów i kroków. Najpopularniejsze
konwencje nazewnictwa to ci.yml (testy), deploy.yml (deployment produkcyjny)
oraz
release.yml (publikacja paczek).
Workflow można uruchomić na różne zdarzenia: push na branch, pull request, tag, cron, ręcznie przez UI (workflow_dispatch) lub przez API. Dla deploymentu produkcyjnego zalecamy trigger na push do main albo tylko na tagi wersji (np. v1.0.0), żeby uniknąć przypadkowych deploy-ów.
Pełny workflow deploy.yml z SSH
Poniżej kompletny workflow deploymentu aplikacji Node.js na VPS. Używa akcji appleboy/ssh-action, która zestawia połączenie SSH na podstawie secretów i wykonuje polecenia na zdalnym
serwerze.
name: Deploy to VPS
on:
push:
branches: [main]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Build
run: |
pnpm install --frozen-lockfile
pnpm build
- name: Deploy via SSH
uses: appleboy/[email protected]
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: 22
script: |
cd /var/www/app
git pull origin main
pnpm install --frozen-lockfile --prod
pnpm build
pm2 reload ecosystem.config.js --update-env Zarządzanie secrets i kluczami SSH
Secrets w GitHub Actions są szyfrowane i dostępne w workflowie przez notację
${{ secrets.NAZWA }}. Do deploymentu potrzebujesz
minimum trzech: host VPS, username i klucz prywatny SSH. Nigdy nie logują się do plików
workflow, ale uważaj na echo — GitHub automatycznie maskuje tylko wartości, które
dokładnie pasują do secretu.
Wygeneruj dedykowany klucz SSH tylko do deploymentu:
# Na lokalnej maszynie
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key
# Dodaj klucz publiczny do VPS
ssh-copy-id -i ~/.ssh/deploy_key.pub [email protected]
# Skopiuj klucz prywatny do GitHub Secret VPS_SSH_KEY
cat ~/.ssh/deploy_key
Na VPS utwórz osobnego użytkownika deploy z ograniczonymi uprawnieniami (np. tylko
do katalogu aplikacji i restart PM2). Nie używaj roota. Dodatkowo warto skonfigurować sudoers tak, żeby użytkownik deploy mógł wykonywać konkretne polecenia bez hasła.
Porównanie typów runnerów
| Runner | Cena/minuta | Użycie | Kiedy wybrać |
|---|---|---|---|
| ubuntu-latest | 0.008 USD | x1 (baseline) | Domyślny — Node/Python/Go |
| windows-latest | 0.016 USD | x2 | Build .NET, MSVC, WinUI |
| macos-latest | 0.08 USD | x10 | iOS/macOS apps |
| self-hosted | darmowy | własny VPS | Dużo buildów, własny hardware |
Matrix builds i caching
Matrix strategy pozwala uruchomić ten sam job równolegle z różnymi parametrami. Najczęstsze zastosowania to testowanie kilku wersji Node.js, Pythona, albo testowanie na różnych systemach operacyjnych. Matrix dramatycznie skraca czas CI — zamiast sekwencyjnie sprawdzać 4 wersje (30 min łącznie), uruchamiamy 4 joby równolegle (7 min łącznie).
Cache to drugi wielki booster wydajności. actions/cache@v4 zapisuje katalogi między
runami workflow, dzięki czemu nie musisz pobierać node_modules przy każdym buildzie. Klucz cache
powinien zawierać hash pliku lock (package-lock.json, pnpm-lock.yaml), żeby automatycznie invalidować
cache po zmianie dependencji.
Dla aplikacji Docker używaj docker/build-push-action@v5 z parametrem cache-from: type=gha i cache-to: type=gha,mode=max — GitHub Actions cache obsługuje layer caching Dockera,
co skraca rebuild image z 8 minut do 30 sekund przy drobnych zmianach kodu.