SuperAPI AI Directory

bitlysis-infrastructure

Deployment & operations

View repository

Updated 10-15 17:25

About this skill

Bitlysis — Infrastructure & DevOps

When to Use

  • Writing or reviewing Dockerfiles
  • Setting up GitHub Actions CI/CD workflows
  • Configuring Render or Vercel deployments
  • Managing environment variables and secrets
  • Adding monitoring, health checks, or observability
  • Working on Dockerfile*, .github/workflows/*.yml, render.yaml, vercel.json

Instructions

Step 1: Docker Multi-Stage Build

# Dockerfile — Next.js frontend (multi-stage, non-root, minimal)
FROM node:22-alpine AS base
WORKDIR /app
RUN corepack enable

FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM node:22-alpine AS runner
WORKDIR /app
# Non-root user (security requirement)
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
ENV PORT=3000 NODE_ENV=production
CMD ["node", "server.js"]
# Dockerfile.api — FastAPI backend
FROM python:3.11-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml ./
RUN uv pip install --system -e ".[prod]"

FROM python:3.11-slim AS runner
WORKDIR /app
RUN useradd -m -u 1001 appuser
COPY --from=builder /usr/local/lib/python3.11 /usr/local/lib/python3.11
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Step 2: GitHub Actions CI/CD

# .github/workflows/ci.yml
name: CI

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

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint-and-test-web:
    name: Web (Lint + Test + Build)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: "9" }
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint:web
      - run: pnpm test --run
      - run: pnpm build:web

  lint-and-test-api:
    name: API (Lint + Test)
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: services/api
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.11" }
      - run: pip install uv && uv pip install --system -e ".[dev]"
      - run: ruff check app tests scripts
      - run: mypy app --strict
      - run: pytest tests -q --cov=app --cov-fail-under=80

Step 3: Environment Variable Management

# .env.example — committed to git (placeholder values only)
# Copy to .env and fill in real values (NEVER commit .env)

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/bitlysis_dev

# Auth
NEXTAUTH_SECRET=your-secret-here-min-32-chars
NEXTAUTH_URL=http://localhost:3000

# LLM APIs
OPENAI_API_KEY=sk-your-key-here
ANTHROPIC_API_KEY=your-key-here

# Storage
S3_BUCKET_NAME=bitlysis-uploads
S3_REGION=ap-southeast-1
// lib/env.ts — validate at startup, fail fast
import { z } from "zod";

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  NEXTAUTH_SECRET: z.string().min(32),
  OPENAI_API_KEY: z.string().startsWith("sk-"),
  NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
});

export const env = envSchema.parse(process.env);

Step 4: Render Deployment (render.yaml)

services:
  - type: web
    name: bitlysis-api
    runtime: python
    plan: starter
    region: singapore
    buildCommand: pip install uv && uv pip install --system -e ".[prod]"
    startCommand: uvicorn app.main:app --host 0.0.0.0 --port $PORT
    healthCheckPath: /health
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: bitlysis-db
          property: connectionString
      - key: OPENAI_API_KEY
        sync: false  # Set manually in Render dashboard

databases:
  - name: bitlysis-db
    plan: starter
    region: singapore
    databaseName: bitlysis
    user: bitlysis_user

Step 5: Vercel Configuration (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Frame-Options", "value": "SAMEORIGIN" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
      ]
    }
  ],
  "rewrites": [
    { "source": "/api/v1/:path*", "destination": "https://bitlysis-api.onrender.com/api/v1/:path*" }
  ]
}

Step 6: Health Checks & Observability

# app/routers/health.py
from fastapi import APIRouter
from sqlalchemy.ext.asyncio import AsyncSession

router = APIRouter()

@router.get("/health")
async def health_check():
    """Liveness probe — is the service running?"""
    return {"status": "ok"}

@router.get("/ready")
async def readiness_check(db: AsyncSession = Depends(get_db)):
    """Readiness probe — is the service ready to serve traffic?"""
    await db.execute(text("SELECT 1"))
    return {"status": "ready", "database": "connected"}

Output Format

For infrastructure changes, provide:

  1. The updated config file
  2. Environment variables needed (with .env.example format)
  3. Verification steps to confirm deployment succeeded

Activate Specialist

@.cursor/.agents/.agents/devinfra-engineer.md