Date: December 6, 2025
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:

  1. Dedicated GitHub repository
  2. Dedicated Vercel project
  3. Subdomain-based dev/staging (under freebeer.ai)
  4. 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
Recommendation: Start with separate repos, use a template to standardize.

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:

  1. Log into Cloudflare
  2. Navigate to: DNS → Records for freebeer.ai
  3. Add CNAME record as above
  4. 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
Note: Some DNS providers support ALIAS or ANAME records for root domains, which is cleaner than A records.
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:

  1. Click "Use this template" → "Create a new repository"
  2. Name it: acme-coffee
  3. Clone locally
  4. Update branding/configuration
  5. Deploy to Vercel
  6. Configure DNS
  7. 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 branding
  • NEXT_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.json with client name
  • Update README.md with client-specific info
  • Initial commit and push

DNS Configuration (Your Side)

  • Add clientname.dev.freebeer.ai CNAME in Cloudflare
  • Add clientname.staging.freebeer.ai CNAME in Cloudflare
  • Wait for DNS propagation (5-15 minutes)
  • Verify: dig clientname.dev.freebeer.ai shows CNAME

Vercel Setup

  • Create new Vercel project linked to GitHub repo
  • Note VERCEL_PROJECT_ID from .vercel/project.json
  • Add domains in Vercel dashboard:
    • clientname.dev.freebeer.ai
    • clientname.staging.freebeer.ai
    • clientdomain.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.com to 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