CI/CD · 2025

CI/CD for Small Teams: Simple Git Push Deployment That Just Works

Updated April 2025 · 9 min read

Test + deploy in under 3 minutes. GitHub Actions + webhook deployment for teams of 1–5 developers.

HomeBlog › CI/CD for Small Teams: Simple Git Push Deployment That Just Works

CI/CD for Small Teams: Simple Git Push Deployment That Just Works

CI/CD ("Continuous Integration, Continuous Deployment") sounds like something only companies with dedicated DevOps engineers can afford to build. Enterprise diagrams with Kubernetes clusters, ArgoCD, Terraform, and eight different YAML files have made it seem complex by default.

For a small team of 1–5 developers, CI/CD doesn't need to be complex. It needs to work reliably and get out of your way. Here's how to build a CI/CD pipeline that a 2-person team can set up in an afternoon and maintain without a dedicated infrastructure engineer.

What CI/CD Actually Means for a Small Team

Continuous Integration (CI):
Every time a developer pushes code, automated tests run. If tests fail, the developer is notified immediately. Broken code doesn't merge to the main branch.

Continuous Deployment (CD):
Every time code is merged to main, it deploys to production automatically. No manual "deployment steps" that someone has to remember to do.

What this prevents:
- "It worked on my machine" bugs caught before they reach production
- Manual deployment steps that get skipped under deadline pressure
- "Who deployed what and when?" mysteries in post-incident debugging
- Accumulated changes that are hard to roll back

For a small team, CI/CD is not about scale — it's about confidence. You can push code on a Friday afternoon and know it won't break the production site unless your tests are also broken.

The Simplest CI/CD Stack That Works

For a small team (1–5 developers), the minimum viable CI/CD pipeline uses two tools:

  1. GitHub Actions — free CI/CD built into GitHub
  2. ApexWeave — managed hosting with webhook-triggered deployments

Total cost: $0 for GitHub Actions (free for public repos; private repos get 2,000 minutes/month free). ApexWeave hosting cost per app.

Setup time: 30–60 minutes for your first pipeline.

Step 1: The Basic Pipeline Structure

Developer pushes code
    ↓
GitHub Actions triggers
    ↓
Tests run
    ↓
If tests pass → Deploy to production
If tests fail → Notify developer, block deployment
    ↓
Production updated automatically

Create .github/workflows/ci-cd.yml in your repository root:

name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  # Step 1: Run tests on every push and PR
  test:
    name: Run Tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test
        env:
          NODE_ENV: test
          # Test-specific env vars only
          DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}

  # Step 2: Deploy only on push to main, only if tests passed
  deploy:
    name: Deploy to Production
    needs: test  # Waits for test job to succeed
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'

    steps:
      - name: Trigger deployment
        run: |
          curl -X POST \
            -H "Content-Type: application/json" \
            "${{ secrets.DEPLOY_WEBHOOK_URL }}"

      - name: Notify team of deployment
        run: |
          echo "Deployed: ${{ github.event.head_commit.message }}"
          echo "Commit: ${{ github.sha }}"
          echo "Author: ${{ github.event.head_commit.author.name }}"

Step 2: Setting Up Secrets

Your workflow file should never contain actual API keys, webhook URLs, or credentials. These go in GitHub Secrets.

Add your deployment webhook URL:
1. ApexWeave dashboard → your app → Git & Deploy tab → copy Webhook URL
2. GitHub repo → Settings → Secrets and variables → Actions → New repository secret
3. Name: DEPLOY_WEBHOOK_URL
4. Value: paste your webhook URL
5. Save

Add other secrets your tests need:
- TEST_DATABASE_URL — connection string for your test database
- TEST_API_KEY — any API keys needed for integration tests

Step 3: Branch Strategy for Small Teams

A simple branching strategy that works with this CI/CD setup:

main          ← production deployments auto-trigger from here
feature/*     ← developer branches (tests run on PR, no deployment)
hotfix/*      ← emergency fixes (can bypass staging with team approval)

Workflow for a new feature:

# Create feature branch
git checkout -b feature/user-authentication

# Develop and commit
git add .
git commit -m "Add JWT authentication"
git push origin feature/user-authentication

# Open PR → tests run automatically
# Review and merge → production deploys automatically

Workflow for a bug fix:

git checkout -b fix/checkout-validation-error
# ... fix the bug ...
git commit -m "Fix: validate email before submitting checkout"
git push origin fix/checkout-validation-error
# Open PR → tests run → merge → production deploys

Adding a Staging Environment

Once you have basic CI/CD working, add a staging environment for testing before production.

Setup:
1. Create a second ApexWeave app for staging
2. Add a staging branch to your repository
3. Add a second webhook secret for staging: STAGING_WEBHOOK_URL

Updated workflow:

on:
  push:
    branches: [main, staging]

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22', cache: 'npm' }
      - run: npm ci
      - run: npm test

  deploy-staging:
    name: Deploy to Staging
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/staging' && github.event_name == 'push'

    steps:
      - name: Deploy to staging
        run: curl -X POST "${{ secrets.STAGING_WEBHOOK_URL }}"

  deploy-production:
    name: Deploy to Production
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'

    steps:
      - name: Deploy to production
        run: curl -X POST "${{ secrets.DEPLOY_WEBHOOK_URL }}"

Your workflow now:

# Feature development
git checkout -b feature/new-dashboard

# Test on staging first
git push origin feature/new-dashboard:staging
# Staging deploys → QA reviews at staging.yourdomain.com

# Promote to production
git checkout main
git merge feature/new-dashboard
git push origin main
# Production deploys automatically

Language-Specific Test Configuration

Node.js (Jest/Vitest)

- name: Run tests
  run: npm test -- --coverage
  env:
    NODE_ENV: test
    DATABASE_URL: postgres://postgres:postgres@localhost:5432/testdb
    JWT_SECRET: test-secret-key

# Add PostgreSQL service for integration tests
services:
  postgres:
    image: postgres:15
    env:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: testdb
    options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    ports:
      - 5432:5432

Python (pytest)

- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'

- name: Install dependencies
  run: pip install -r requirements.txt

- name: Run tests
  run: pytest --tb=short -q
  env:
    DJANGO_SETTINGS_MODULE: myproject.settings.test
    DATABASE_URL: postgres://postgres:postgres@localhost:5432/testdb
    SECRET_KEY: test-secret-key

PHP (Laravel + PHPUnit)

- name: Set up PHP
  uses: shivammathur/setup-php@v2
  with:
    php-version: '8.3'
    extensions: pdo, pdo_mysql, mbstring

- name: Install Composer dependencies
  run: composer install --no-interaction --prefer-dist

- name: Copy .env.testing
  run: cp .env.testing .env

- name: Generate app key
  run: php artisan key:generate

- name: Run migrations
  run: php artisan migrate --env=testing

- name: Run tests
  run: php artisan test --parallel

Ruby on Rails (RSpec)

- name: Set up Ruby
  uses: ruby/setup-ruby@v1
  with:
    ruby-version: '3.3'
    bundler-cache: true

- name: Set up database
  run: |
    bundle exec rails db:create RAILS_ENV=test
    bundle exec rails db:schema:load RAILS_ENV=test

- name: Run tests
  run: bundle exec rspec --format progress
  env:
    RAILS_ENV: test
    DATABASE_URL: postgres://postgres:postgres@localhost:5432/testdb

Deployment Notifications (Highly Recommended)

Know immediately when a deployment succeeds or fails:

Slack notification:

  - name: Deployment success
    if: success()
    uses: slackapi/slack-github-action@v1.26.0
    with:
      payload: |
        {
          "text": "✅ *Deployed to production*\n*Commit:* ${{ github.event.head_commit.message }}\n*Author:* ${{ github.event.head_commit.author.name }}\n*SHA:* `${{ github.sha }}`"
        }
    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

  - name: Deployment failure
    if: failure()
    uses: slackapi/slack-github-action@v1.26.0
    with:
      payload: |
        {
          "text": "❌ *Deployment FAILED*\n*Commit:* ${{ github.event.head_commit.message }}\n*Author:* ${{ github.event.head_commit.author.name }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View failed run>"
        }
    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

When to Add More to Your Pipeline

Start minimal. Add complexity only when you have a specific problem that simpler alternatives don't solve.

Your current pain → What to add:

Pain Point Addition
Tests are slow (>5 min) Parallelise: test --shard=1/4 etc.
Different features conflict Feature flags (env vars) instead of branches
Need to test against multiple DB versions Matrix strategy in GitHub Actions
Production deploys during business hours Schedule deploys: cron: '0 2 * * *'
Security vulnerabilities in dependencies npm audit or snyk step
Code style inconsistency ESLint/Prettier/phpstan in CI
Performance regressions Lighthouse CI step

What you probably don't need:
- Kubernetes, Helm charts, ArgoCD — for a team under 20 people
- Separate build/push Docker images to registry — managed platforms handle this
- Terraform or Pulumi for infrastructure — managed hosting eliminates this layer
- Multiple staging environments — one staging + production is enough for most teams

What Your Pipeline Tells You

After this CI/CD setup is running, your team gains operational clarity you didn't have before:

From the GitHub Actions dashboard:
- Every commit's test result — green or red, immediately
- Deployment history with commit SHAs and timestamps
- Which branch triggered each deployment
- How long tests and deployments take (track this over time — if it's growing, address it)

From the ApexWeave dashboard:
- Every deployment with build logs
- Rollback history (which deploys were rolled back and when)
- Uptime history (did a deployment cause downtime?)
- Activity log (all environment variable changes, domain updates)

Combined: Full audit trail. When something breaks in production, you know exactly which commit caused it, who made the change, when it deployed, and you can roll back in one click.

The One Thing Most Small Teams Get Wrong About CI/CD

Most teams set up CI/CD and then add too many steps to the pipeline over time: linting, security scans, integration tests, performance tests, end-to-end tests, dependency audits...

The pipeline becomes slow (10+ minutes) and teams start merging to main without waiting for CI. When the pipeline takes longer than a developer's patience, it stops being a safety net.

Keep your CI pipeline under 3 minutes. For small apps:
- Unit tests: 30–60 seconds
- Integration tests (with a real test database): 60–120 seconds
- Build: 30–60 seconds
- Deploy webhook: 2–5 seconds

If your tests take longer, parallelise them or split them across jobs that run concurrently.

Fast CI/CD is used CI/CD. Slow CI/CD gets bypassed.

Set up your deployment target at apexweave.com/git-deployment.php — webhook auto-deployment, build logs, rollback, and activity audit for every push.

Deploy Your App with Git Push

Automatic builds, environment variables, live logs, rollback, and custom domains. No server management required.

Deploy Free — No Card Required

Deploy Your App with Git Push

Automatic builds, environment variables, live logs, rollback, and custom domains. No server management required.

Deploy Free — No Card Required

Powered by WHMCompleteSolution