Smithery Logo
MCPsSkillsDocsPricing
Login
NewFlame, an assistant that learns and improves. Available onTelegramSlack
    jeremylongshore

    customerio-deploy-pipeline

    jeremylongshore/customerio-deploy-pipeline
    DevOps
    1,221

    About

    SKILL.md

    Install

    • Telegram
      Telegram
    • Slack
      Slack
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    • Download skill
    ├─
    ├─
    └─
    Smithery Logo

    Give agents more agency

    Resources

    DocumentationPrivacy PolicySystem Status

    Company

    PricingAboutBlog

    Connect

    © 2026 Smithery. All rights reserved.

    About

    Deploy Customer.io integrations to production. Use when deploying to cloud platforms, setting up production infrastructure, or automating deployments. Trigger with phrases like "deploy customer.io",...

    SKILL.md

    Customer.io Deploy Pipeline

    Overview

    Deploy Customer.io integrations to production: GCP Cloud Run with Secret Manager, Vercel serverless functions, AWS Lambda with SSM, Kubernetes with external secrets, plus health check endpoints and blue-green deployment scripts.

    Prerequisites

    • CI/CD pipeline configured (see customerio-ci-integration)
    • Cloud platform credentials and access
    • Production Customer.io credentials in a secrets manager

    Instructions

    Step 1: Deploy to Google Cloud Run

    # .github/workflows/deploy-cloud-run.yml
    name: Deploy to Cloud Run
    on:
      push:
        branches: [main]
    
    jobs:
      deploy:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          id-token: write  # Required for Workload Identity Federation
        steps:
          - uses: actions/checkout@v4
    
          - id: auth
            uses: google-github-actions/auth@v2
            with:
              workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
              service_account: ${{ secrets.WIF_SA }}
    
          - uses: google-github-actions/setup-gcloud@v2
    
          - name: Build and push
            run: |
              gcloud builds submit --tag gcr.io/${{ secrets.GCP_PROJECT }}/cio-service
    
          - name: Deploy
            run: |
              gcloud run deploy cio-service \
                --image gcr.io/${{ secrets.GCP_PROJECT }}/cio-service \
                --region us-central1 \
                --set-secrets "CUSTOMERIO_SITE_ID=cio-site-id:latest,\
                  CUSTOMERIO_TRACK_API_KEY=cio-track-key:latest,\
                  CUSTOMERIO_APP_API_KEY=cio-app-key:latest" \
                --set-env-vars "CUSTOMERIO_REGION=us,NODE_ENV=production" \
                --min-instances 1 \
                --max-instances 10 \
                --memory 512Mi \
                --cpu 1 \
                --allow-unauthenticated
    

    Step 2: Health Check Endpoint

    // routes/health.ts
    import { TrackClient, RegionUS } from "customerio-node";
    import { Router } from "express";
    
    const router = Router();
    
    router.get("/health", async (_req, res) => {
      const checks: Record<string, { status: string; latency_ms?: number }> = {};
    
      // Check Track API
      const cio = new TrackClient(
        process.env.CUSTOMERIO_SITE_ID!,
        process.env.CUSTOMERIO_TRACK_API_KEY!,
        { region: RegionUS }
      );
    
      const start = Date.now();
      try {
        await cio.identify("health-check", {
          email: "health@internal.example.com",
          _health_check: true,
        });
        checks.track_api = { status: "ok", latency_ms: Date.now() - start };
      } catch (err: any) {
        checks.track_api = { status: `error: ${err.statusCode}` };
      }
    
      const allOk = Object.values(checks).every((c) => c.status === "ok");
    
      res.status(allOk ? 200 : 503).json({
        status: allOk ? "healthy" : "degraded",
        checks,
        version: process.env.npm_package_version ?? "unknown",
        uptime_seconds: Math.floor(process.uptime()),
        timestamp: new Date().toISOString(),
      });
    });
    
    export default router;
    

    Step 3: Vercel Serverless Functions

    // api/customerio/identify.ts (Vercel serverless function)
    import type { VercelRequest, VercelResponse } from "@vercel/node";
    import { TrackClient, RegionUS } from "customerio-node";
    
    const cio = new TrackClient(
      process.env.CUSTOMERIO_SITE_ID!,
      process.env.CUSTOMERIO_TRACK_API_KEY!,
      { region: RegionUS }
    );
    
    export default async function handler(req: VercelRequest, res: VercelResponse) {
      if (req.method !== "POST") {
        return res.status(405).json({ error: "Method not allowed" });
      }
    
      const { userId, attributes } = req.body;
      if (!userId || !attributes?.email) {
        return res.status(400).json({ error: "userId and attributes.email required" });
      }
    
      try {
        await cio.identify(userId, {
          ...attributes,
          last_seen_at: Math.floor(Date.now() / 1000),
        });
        return res.status(200).json({ success: true });
      } catch (err: any) {
        return res.status(err.statusCode ?? 500).json({ error: err.message });
      }
    }
    

    Step 4: Kubernetes Deployment

    # k8s/customerio-service.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: customerio-service
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: customerio-service
      template:
        metadata:
          labels:
            app: customerio-service
        spec:
          containers:
            - name: app
              image: gcr.io/my-project/cio-service:latest
              ports:
                - containerPort: 3000
              env:
                - name: CUSTOMERIO_SITE_ID
                  valueFrom:
                    secretKeyRef:
                      name: customerio-secrets
                      key: site-id
                - name: CUSTOMERIO_TRACK_API_KEY
                  valueFrom:
                    secretKeyRef:
                      name: customerio-secrets
                      key: track-api-key
                - name: CUSTOMERIO_APP_API_KEY
                  valueFrom:
                    secretKeyRef:
                      name: customerio-secrets
                      key: app-api-key
                - name: CUSTOMERIO_REGION
                  value: "us"
                - name: NODE_ENV
                  value: "production"
              resources:
                requests:
                  cpu: 100m
                  memory: 256Mi
                limits:
                  cpu: 500m
                  memory: 512Mi
              readinessProbe:
                httpGet:
                  path: /health
                  port: 3000
                initialDelaySeconds: 5
                periodSeconds: 10
              livenessProbe:
                httpGet:
                  path: /health
                  port: 3000
                initialDelaySeconds: 15
                periodSeconds: 30
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: customerio-service
    spec:
      selector:
        app: customerio-service
      ports:
        - port: 80
          targetPort: 3000
    

    Step 5: Blue-Green Deployment

    #!/usr/bin/env bash
    set -euo pipefail
    # scripts/blue-green-deploy.sh
    
    SERVICE="cio-service"
    REGION="us-central1"
    IMAGE="gcr.io/${GCP_PROJECT}/${SERVICE}:${COMMIT_SHA}"
    
    echo "=== Blue-Green Deploy: ${SERVICE} ==="
    
    # 1. Deploy with no traffic
    gcloud run deploy "${SERVICE}" \
      --image "${IMAGE}" \
      --region "${REGION}" \
      --no-traffic \
      --tag "canary"
    
    echo "Deployed canary. Running health check..."
    
    # 2. Health check on canary
    CANARY_URL=$(gcloud run services describe "${SERVICE}" \
      --region "${REGION}" --format 'value(status.url)' \
      | sed 's|https://|https://canary---|')
    
    HEALTH=$(curl -s -o /dev/null -w "%{http_code}" "${CANARY_URL}/health")
    if [ "${HEALTH}" != "200" ]; then
      echo "FAIL: Health check returned ${HEALTH}. Aborting."
      exit 1
    fi
    
    # 3. Shift traffic: 10% → 50% → 100%
    for pct in 10 50 100; do
      echo "Shifting ${pct}% traffic to canary..."
      gcloud run services update-traffic "${SERVICE}" \
        --region "${REGION}" \
        --to-tags "canary=${pct}"
      sleep 30
    done
    
    echo "Deploy complete. 100% traffic on new revision."
    

    Deployment Checklist

    • Production secrets in secrets manager (not env files)
    • Health check endpoint responds 200
    • Readiness and liveness probes configured
    • Resource limits set (CPU, memory)
    • Min instances > 0 (avoid cold starts)
    • Blue-green or canary deployment configured
    • Rollback procedure documented
    • Post-deploy smoke test automated

    Error Handling

    Issue Solution
    Secret not found Verify secret name in secrets manager
    Health check timeout Increase initialDelaySeconds, check CIO connectivity
    Cold start latency Set --min-instances 1 (Cloud Run) or keep-alive
    Memory OOM Increase memory limits, check for event queue buildup

    Resources

    • Cloud Run Documentation
    • Vercel Serverless Functions
    • Kubernetes Secrets

    Next Steps

    After deployment, proceed to customerio-webhooks-events for webhook handling.

    Recommended Servers
    Attio
    Attio
    EasyWeek
    EasyWeek
    Supabase
    Supabase
    Repository
    jeremylongshore/claude-code-plugins-plus-skills
    Files