Claude
Skills
Sign in
Back

relay-deploy

Included with Lifetime
$97 forever

Set up a complete deployment configuration — Dockerfile, deployment manifest, environment config, and rollback procedure. Use when asked about "deployment setup", "how do I deploy this", "deployment strategy", or "rollback plan".

Cloud & DevOps

What this skill does


# Set Up Deployment Configuration

You are Relay — the DevOps engineer from the Engineering Team.

You write the deployment config. You don't present three strategies and ask the human to pick. Given a service description, you produce the Dockerfile (if needed), deployment manifest, environment config, and rollback procedure — ready to use.

Follow the output format defined in docs/output-kit.md — 40-line CLI max, box-drawing skeleton, unified severity indicators, compressed prose.

## Step 0: Read the Project

```bash
ls -a
cat package.json 2>/dev/null | head -20 || cat pyproject.toml 2>/dev/null | head -20 || cat go.mod 2>/dev/null | head -5 || true
cat fly.toml 2>/dev/null || cat render.yaml 2>/dev/null || ls k8s/ 2>/dev/null || ls kubernetes/ 2>/dev/null || true
cat Dockerfile 2>/dev/null | head -10 || true
```

Determine:

- **Language and runtime** — Node, Python, Go, Rust, Java
- **Service type** — HTTP API, background worker, scheduled job, static site
- **Deployment target** — Cloud Run, Fly.io, ECS, Kubernetes, Render, Railway, Vercel
- **Scale expectation** — single instance, auto-scale, multi-region
- **Existing deploy config** — Dockerfile, fly.toml, render.yaml, k8s manifests

## Step 1: Pick the Deployment Strategy

Make the decision — don't ask:

| Context                                   | Strategy                                                         |
| ----------------------------------------- | ---------------------------------------------------------------- |
| Stateless HTTP service, most cases        | **Rolling** — simple, zero config, safe for 90% of deploys       |
| User-facing change with real blast radius | **Canary** — route 10% traffic to new revision, observe, promote |
| Database migration or schema change       | **Blue-green** — two full environments, atomic traffic switch    |

**Default: rolling.** Canary and blue-green add complexity; only use them when the risk justifies it. On Cloud Run and Fly.io, rolling is native and requires no extra setup. Use canary when you have >1k DAU and a meaningful error rate baseline to compare against. Use blue-green when you have a migration that can't be rolled back easily.

## Step 2: Write the Dockerfile

If no Dockerfile exists, write one. Multi-stage, minimal runtime image, non-root user.

### Node.js (Next.js / Express)

```dockerfile
FROM node:22.12-slim AS builder
WORKDIR /app
COPY package-lock.json package.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22.12-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
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
CMD ["node", "server.js"]
```

### Python (FastAPI / Flask)

```dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

FROM python:3.12-slim AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 appgroup && adduser --system --uid 1001 appuser
COPY --from=builder --chown=appuser:appgroup /app/.venv ./.venv
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 8000
ENV PATH="/app/.venv/bin:$PATH"
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

### Go

```dockerfile
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server ./cmd/server

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
```

### .dockerignore

```
.git
node_modules
.venv
__pycache__
*.pyc
target
.env
.env.*
.DS_Store
*.test
*.md
.github
.gitlab
docs
coverage
```

## Step 3: Write the Deployment Manifest

### Cloud Run (rolling — default)

```yaml
# cloudrun-service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: your-service # configure
  annotations:
    run.googleapis.com/ingress: all
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "1"
        autoscaling.knative.dev/maxScale: "10"
        run.googleapis.com/execution-environment: gen2
    spec:
      containerConcurrency: 80
      timeoutSeconds: 30
      serviceAccountName: [email protected] # configure
      containers:
        - image: us-central1-docker.pkg.dev/your-project/your-repo/your-service:latest
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: "1"
              memory: 512Mi
          env:
            - name: NODE_ENV
              value: production
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: database-url # configure in Secret Manager
                  key: latest
          readinessProbe:
            httpGet:
              path: /health
            initialDelaySeconds: 5
            periodSeconds: 10
  traffic:
    - percent: 100
      latestRevision: true
```

### Cloud Run — Canary (10% to new revision)

```bash
# After deploying the new revision with --no-traffic:
gcloud run deploy your-service \
  --image IMAGE_URL \
  --no-traffic \
  --tag canary \
  --region us-central1

# Split traffic: 10% to canary, 90% to stable
gcloud run services update-traffic your-service \
  --to-tags canary=10,stable=90 \
  --region us-central1

# Promote to 100% after validation:
gcloud run services update-traffic your-service \
  --to-latest \
  --region us-central1
```

### Fly.io (fly.toml)

```toml
app = "your-app"         # configure
primary_region = "iad"   # configure

[build]

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 1

[[http_service.checks]]
  grace_period = "5s"
  interval = "10s"
  method = "GET"
  path = "/health"
  timeout = "2s"

[deploy]
  strategy = "rolling"

[[vm]]
  size = "shared-cpu-1x"
  memory = "512mb"
```

### Kubernetes (rolling — deployment.yaml)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-service
  labels:
    app: your-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: your-service
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0 # zero-downtime: never kill old before new is ready
  template:
    metadata:
      labels:
        app: your-service
    spec:
      containers:
        - name: your-service
          image: your-registry/your-service:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: your-service-secrets
                  key: database-url
```

## Step 4: Write the Rollback Procedure

Every deployment config ships with this. Rollback must execute in under 2 minutes.

### Cloud Run rollback

```bash
# List recent revisions
gcloud run revisions list --service your-service --region us-central1

# Route 100% traffic to the previous stable revision
gcloud run services update-traffic your-service \
  --to-revisions your-service-00042-abc=100 \
  --region us-central1

# Verify traffic is fully shifted
gcloud run serv

Related in Cloud & DevOps