Multi-Client Development Pipeline Guide
Scale the PAI Dashboard CI/CD pipeline to support multiple client projects
Purpose: Complete guide for managing multiple client projects with professional dev → staging → production pipeline
Architecture Overview
The Challenge
You need to build multiple client applications with the same dev → staging → production pipeline, while maintaining:
- Isolation between client projects
- Professional development workflow
- Clear handoff to client domains at production
The Solution
Each client gets:
- Dedicated GitHub repository
- Dedicated Vercel project
- Subdomain-based dev/staging (under freebeer.ai)
- Client's domain for production
DNS Structure
Pattern for Each Client
┌─────────────────────────────────────────────────────────────┐
│ CLIENT: Acme Coffee Shop │
│ Repository: freebeerstudio/acme-coffee │
│ Vercel Project: acme-coffee │
└─────────────────────────────────────────────────────────────┘
Development: acme.dev.freebeer.ai
↳ DNS: Cloudflare (freebeer.ai)
↳ CNAME → cname.vercel-dns.com
↳ You control entirely
Staging: acme.staging.freebeer.ai
↳ DNS: Cloudflare (freebeer.ai)
↳ CNAME → cname.vercel-dns.com
↳ Show clients for UAT
Production: acmecoffee.com
↳ DNS: Client's registrar (GoDaddy, etc.)
↳ CNAME www → cname.vercel-dns.com
↳ A @ → 76.76.21.21 (Vercel)
↳ Client owns domain
Why This Pattern
Your Subdomains (dev/staging):
- You maintain full control during development
- No coordination needed with client
- Easy to create/destroy as projects come and go
- Professional preview URLs for client feedback
Client's Domain (production):
- Client owns their brand
- No dependency on Free Beer Studio after handoff
- Standard industry practice
- Client can move to different host if needed
Repository Structure
Option 1: Separate Repos (Recommended)
GitHub Organization: freebeerstudio
freebeerstudio/
├── pai-dashboard/ # Your internal dashboard
├── acme-coffee/ # Client: Acme Coffee Shop
├── wellness-center/ # Client: Wellness Center
├── local-bakery/ # Client: Local Bakery
└── client-template/ # Template for new clients
Advantages:
- Clean isolation between clients
- Separate access control (invite client to their repo only)
- Independent deployment histories
- Easy to archive/transfer when project ends
- GitHub Actions minutes tracked per repo
Disadvantages:
- More repos to manage
- Need to replicate workflow files (solved with templates)
Option 2: Monorepo
freebeerstudio/client-sites/
├── apps/
│ ├── acme-coffee/
│ ├── wellness-center/
│ └── local-bakery/
├── packages/
│ └── shared-components/
└── .github/workflows/
Advantages:
- Share code/components easily
- Single place for common configurations
- Easier dependency updates
Disadvantages:
- Complex deployment setup
- Hard to give clients access to just their project
- All or nothing repository access
DNS Configuration by Environment
1. Development (*.dev.freebeer.ai)
Your Cloudflare DNS:
Record Type: CNAME
Name: acme.dev
Value: cname.vercel-dns.com
TTL: 300 (5 minutes)
Steps:
- Log into Cloudflare
- Navigate to: DNS → Records for freebeer.ai
- Add CNAME record as above
- Repeat for each client (
clientname.dev)
Result: acme.dev.freebeer.ai → Vercel → Deployed app
2. Staging (*.staging.freebeer.ai)
Your Cloudflare DNS:
Record Type: CNAME
Name: acme.staging
Value: cname.vercel-dns.com
TTL: 300
Same process as dev, just different subdomain.
3. Production (clientdomain.com)
Client's DNS (they manage):
Two options depending on their setup:
Option A: Subdomain (www.acmecoffee.com)
Record Type: CNAME
Name: www
Value: cname.vercel-dns.com
TTL: 3600
Option B: Root Domain (acmecoffee.com)
Record Type: A
Name: @ (or blank)
Value: 76.76.21.21
TTL: 3600
Record Type: CNAME
Name: www
Value: cname.vercel-dns.com
TTL: 3600
Vercel IP Address: 76.76.21.21 (verify current IP at Vercel docs)
Vercel Project Setup
For Each Client
1. Create New Project:
cd ~/PAI/Workshop/client-sites/acme-coffee
vercel --prod
2. Add Domains in Vercel Dashboard:
- Go to: https://vercel.com/freebeerstudio/acme-coffee/settings/domains
- Add three domains:
acme.dev.freebeer.ai(Development)acme.staging.freebeer.ai(Staging)acmecoffee.com(Production)
3. Configure Environment Variables:
- Development: Set
NEXT_PUBLIC_APP_URL=https://acme.dev.freebeer.ai - Staging: Set
NEXT_PUBLIC_APP_URL=https://acme.staging.freebeer.ai - Production: Set
NEXT_PUBLIC_APP_URL=https://acmecoffee.com
GitHub Repository Template
Create a Template Repository
1. Create new repo: freebeerstudio/client-template
2. Include:
client-template/
├── .github/workflows/
│ ├── dev-deploy.yml
│ ├── staging-deploy.yml
│ └── production-deploy.yml
├── app/ # Next.js app directory
├── public/
├── package.json
├── .env.example
├── .gitignore
├── README.md
└── SETUP.md # Client-specific setup instructions
3. Mark as template: Settings → Template repository ✓
Using the Template
For each new client:
- Click "Use this template" → "Create a new repository"
- Name it:
acme-coffee - Clone locally
- Update branding/configuration
- Deploy to Vercel
- Configure DNS
- Add GitHub Secrets
GitHub Actions Workflows
Template Workflow Pattern
Each client repo needs the same three workflows with client-specific domains.
Example: dev-deploy.yml
name: Deploy to Development
on:
push:
branches:
- main
jobs:
deploy-dev:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
env:
NODE_ENV: production
# CLIENT-SPECIFIC: Update these for each project
NEXT_PUBLIC_APP_NAME: "Acme Coffee Shop - Dev"
NEXT_PUBLIC_APP_URL: "https://acme.dev.freebeer.ai"
- name: Deploy to Vercel (Development)
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
- name: Comment on commit
if: success()
run: |
echo "✅ Successfully deployed to acme.dev.freebeer.ai"
Key Changes Per Client:
NEXT_PUBLIC_APP_NAME: Client brandingNEXT_PUBLIC_APP_URL: Client's dev domain- Success message: Client's dev URL
Setup Checklist for New Client
Pre-Development
- Client meeting - gather requirements
- Domain name confirmed (for production)
- Brand assets received (logo, colors, fonts)
- Content/copy provided or scheduled
Repository Setup
- Create repo from template:
freebeerstudio/client-name - Clone locally:
git clone git@github.com:freebeerstudio/client-name.git - Update
package.jsonwith client name - Update
README.mdwith client-specific info - Initial commit and push
DNS Configuration (Your Side)
- Add
clientname.dev.freebeer.aiCNAME in Cloudflare - Add
clientname.staging.freebeer.aiCNAME in Cloudflare - Wait for DNS propagation (5-15 minutes)
- Verify:
dig clientname.dev.freebeer.aishows CNAME
Vercel Setup
- Create new Vercel project linked to GitHub repo
- Note
VERCEL_PROJECT_IDfrom.vercel/project.json - Add domains in Vercel dashboard:
clientname.dev.freebeer.aiclientname.staging.freebeer.aiclientdomain.com(add later, before production deploy)
- Configure environment variables for each environment
GitHub Actions
- Add GitHub Secrets:
VERCEL_TOKEN(same for all clients)VERCEL_ORG_ID(same for all clients)VERCEL_PROJECT_ID(unique per client)
- Update workflow files with client-specific URLs
- Test dev auto-deploy with test commit
Pre-Production
- Client reviews staging site
- Client approves for production
- Client provides access to their DNS (or DNS instructions)
- Schedule production deployment
Production Launch
- Add
clientdomain.comto Vercel project - Client configures DNS (CNAME or A record)
- Wait for DNS propagation + SSL provisioning
- Create production tag:
git tag -a v1.0.0 -m "Production launch" - Push tag:
git push origin v1.0.0 - Verify production site is live
- Celebrate!
Example: Full Client Setup
Acme Coffee Shop
Client Info:
- Business: Acme Coffee Shop
- Production Domain:
acmecoffee.com - Repository:
freebeerstudio/acme-coffee
1. Create Repository
# Use template on GitHub UI
# Clone locally
cd ~/PAI/Workshop/client-sites
git clone git@github.com:freebeerstudio/acme-coffee.git
cd acme-coffee
2. Configure DNS (Cloudflare)
Add two CNAME records to freebeer.ai:
Name: acme.dev Value: cname.vercel-dns.com
Name: acme.staging Value: cname.vercel-dns.com
3. Deploy to Vercel
# First deployment
vercel --prod
# Follow prompts, link to new project
# Note the project ID
cat .vercel/project.json
# Copy projectId: prj_xxxxx
4. Add Domains in Vercel
Dashboard → acme-coffee → Settings → Domains:
acme.dev.freebeer.ai
acme.staging.freebeer.ai
(acmecoffee.com - add later)
5. Add GitHub Secrets
Settings → Secrets and variables → Actions:
VERCEL_TOKEN=trq9OpaIp7ZprCnXEp7edunz
VERCEL_ORG_ID=team_1EitYv6CCRydiKfiQvChvQS0
VERCEL_PROJECT_ID=prj_xxxxx (from step 3)
6. Update Workflows
Edit .github/workflows/dev-deploy.yml:
env:
NEXT_PUBLIC_APP_NAME: "Acme Coffee Shop - Dev"
NEXT_PUBLIC_APP_URL: "https://acme.dev.freebeer.ai"
Repeat for staging-deploy.yml and production-deploy.yml.
7. Test Dev Deployment
git add .
git commit -m "Initial setup for Acme Coffee"
git push origin main
# Watch workflow
gh run watch
Verify at: https://acme.dev.freebeer.ai
8. Before Production
Client configures DNS for acmecoffee.com:
Record Type: CNAME
Name: www
Value: cname.vercel-dns.com
Record Type: A
Name: @
Value: 76.76.21.21
Add domain in Vercel, wait for SSL.
9. Production Deploy
git tag -a v1.0.0 -m "Acme Coffee production launch"
git push origin v1.0.0
Site live at: https://acmecoffee.com
Scaling Considerations
Managing Multiple Clients
Organization:
~/PAI/Workshop/client-sites/
├── acme-coffee/
├── wellness-center/
├── local-bakery/
└── template/
DNS Management
- Subdomain Limit: Cloudflare allows unlimited subdomains
- Organization: Use consistent naming:
clientname.dev.freebeer.ai - Document in spreadsheet or tracking file
Vercel Project Limits
| Tier | Cost | Deployments |
|---|---|---|
| Free | $0 | 100/month per project |
| Pro | ~$20/month | Unlimited |
Strategy:
- Each client = separate Vercel project
- Bill client for Vercel Pro if needed
- Or: Bundle hosting into your service fee
GitHub Actions Minutes
- Free Tier: 2,000 minutes/month
- Each deployment: ~1-2 minutes
Estimate:
- 10 clients × 20 deploys/month × 2 min = 400 minutes
- Well within free tier
- Scales to ~30-40 active clients before needing paid plan
Best Practices
Repository Management
- Use templates: Standardize setup, save time
- Branch protection: Require PR reviews before merging to main
- Semantic versioning: Tag releases properly (v1.0.0, v1.1.0)
- README: Client-specific setup and maintenance docs
DNS Management
- Document everything: Keep spreadsheet of all domains
- TTL settings: Use 300 (5 min) for dev, 3600 (1 hour) for production
- Subdomain naming: Keep it simple and consistent
- SSL certs: Let Vercel handle it (automatic)
Client Communication
- Share staging URL: Let them see work in progress
- UAT on staging: Get formal approval before production
- Production checklist: Walk through launch steps together
- Training: Show them how to request updates
Troubleshooting
"Domain already exists in another project"
Issue: You added domain to wrong Vercel project
Fix: Remove from other project first, then add to correct one
"SSL provisioning failed"
Issue: DNS not configured correctly
Fix: Verify CNAME/A record, wait 24 hours for propagation
"Workflow failing on client repo"
Issue: GitHub Secrets not set
Fix: Add all three secrets (TOKEN, ORG_ID, PROJECT_ID)
"Client can't see their domain"
Issue: DNS not propagated yet
Fix: Check with dig clientdomain.com, wait longer