GitHub Actions — built-in CI/CD in GitHub (free 2000 min/month for private repos, unlimited for public). Workflow = YAML file in .github/workflows/. Triggers: push, pull_request, schedule (cron), manual. Runners: ubuntu-latest, macos-latest, windows-latest. Deploy via SSH + rsync, docker push, Vercel/Netlify integrations. Secrets stored in repo settings.
Below: step-by-step, working examples, common pitfalls, FAQ.
.github/workflows/ci.ymlon: push: branches: [main]| Scenario | Config |
|---|---|
| Simple Node.js CI | name: CI
on:
push: { branches: [main] }
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm test |
| Deploy via SSH | deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: cd /var/www && git pull && npm install --production && pm2 reload all |
| Matrix build (multi Node version) | strategy:
matrix:
node: [18, 20, 22]
steps:
- uses: actions/setup-node@v4
with: { node-version: ${{ matrix.node }} } |
| Docker build + push | - uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }} |
| Scheduled workflow (cron) | on:
schedule:
- cron: '0 2 * * *' # daily at 2 AM UTC |
::add-mask:: or sensitive steps with if: env checkif: github.ref == 'refs/heads/main'For solo project — yes. Team 5+ — sometimes need Teams $4/user/mo or self-hosted runners.
For private network access, unlimited minutes, GPU/ARM runners. Downside — maintenance + security (compromised runner = RCE in workflow).
Secrets — encrypted, not visible in logs. Env vars — plain, visible. For tokens/passwords — always secrets.
GitHub Actions: free for GH repos, huge actions marketplace, YAML. Jenkins: self-host, flexible but maintenance. GitLab CI: tight GitLab integration. For GitHub — Actions default.