A Lean CI/CD Pipeline for Solo Developers: Automated Testing and Deployments with Docker & GitHub Actions
Solo developers and startup founders looking for simple, robust deployment workflows without heavy cloud infrastructure.
When you are a solo developer or working in a small startup team, you do not need a 400-line Terraform configuration or a $500/month Kubernetes cluster. What you need is:
- Confidence: Automatic unit & integration tests run on every push so you don't ship broken code.
- Speed: Fast Docker image builds with layer caching.
- Simplicity: Automatic zero-downtime deployment to your VPS the moment a PR merges into
main.
Here is the exact, battle-tested GitHub Actions CI/CD workflow I use across my production projects.
1. The 3-Step Pipeline Strategy
git push origin main
│
â–¼
[Stage 1: Lint & Test] (Runs in parallel with cache)
│ (Passes)
â–¼
[Stage 2: Build & Push Docker Image] (BuildKit + GitHub Cache)
│ (Passes)
â–¼
[Stage 3: SSH Deploy to VPS] (Pulls new image, runs migrations, reloads Nginx)
2. The Complete .github/workflows/deploy.yml
name: CI/CD Production Pipeline
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js / Bun
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Linter & Typecheck
run: npm run typecheck
- name: Run Test Suite
run: npm test
build-and-deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry (GHCR)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push Multi-Arch Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Deploy via SSH to VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /opt/apps/my-app
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
3. Why This Workflow Excels for Solo Engineers
- GitHub Cache (
type=gha): Docker layers are cached directly inside GitHub Actions. Unchanged dependency layers are skipped, reducing build times from 6 minutes to under 45 seconds. - Zero Secrets Leakage: Deployments authenticate via ephemeral
GITHUB_TOKENand SSH private keys stored in encrypted repository secrets. - Auto Image Pruning:
docker image prune -fprevents your VPS disk from filling up with orphaned container images over time.
Share this article
Adam Abdel-Djamal
Full Stack DeveloperAdam Abdel-Djamal is a Full-Stack & AI Engineer crafting high-performance web applications with Nuxt, Vue, Laravel, and autonomous AI agents.
Related Articles
6 Common API Mistakes (And How to Fix Them)
From wrong HTTP methods to leaking sensitive data — the errors most developers make when building their first API, and the concrete fixes.
API Rate Limiting in Practice: Token Bucket vs. Sliding Window with Redis
Comparing rate-limiting algorithms to protect your backend against scrapers, credential stuffing, and bot spam with minimal latency overhead.