Deploy no GCP Cloud Run com Claude Code: guia prático
Publique uma API Node.js segura no Cloud Run com Docker, IAM, secrets, logs e rollback.
Cloud Run é uma boa escolha quando você quer publicar um serviço HTTP em contêiner sem operar Kubernetes, ECS ou uma frota de VMs. Diferente de Cloud Functions ou Lambda, você não precisa transformar tudo em pequenas funções. Uma API Express comum pode virar uma imagem Docker, e o Google Cloud cuida de HTTPS, revisões, escala e logs.
Este guia mostra como usar Claude Code para levar uma API TypeScript/Express do ambiente local até o Cloud Run. Ele inclui código executável, Dockerfile, teste local, Artifact Registry, gcloud run deploy, service account, IAM, Secret Manager, concorrência, instâncias mínimas, Cloud Logging, rollback de revisão, armadilhas de custo e uma prompt de revisão de deploy.
Em linguagem simples: Cloud Run é uma plataforma serverless para executar contêineres que respondem a HTTP. Artifact Registry é o repositório de imagens Docker. Secret Manager é o cofre de senhas e chaves de API. IAM é o sistema de permissões que define qual identidade acessa qual recurso.
Quando Cloud Run vence Functions ou Lambda
Cloud Run nem sempre é melhor que Cloud Functions ou Lambda. Para um handler de evento minúsculo, uma função pode ser mais simples. Cloud Run ganha quando o serviço já é uma aplicação HTTP ou quando você precisa de mais controle sobre runtime e dependências.
| Caso de uso | Por que Cloud Run encaixa |
|---|---|
| Receptor de webhooks | Stripe, GitHub e LINE viram rotas Express naturalmente |
| BFF ou API pequena | Auth, middleware, rotas e validação ficam em uma app Node.js normal |
| Endpoint HTTP para batch agendado | Cloud Scheduler pode chamar via HTTP sem outro framework de jobs |
| API leve de IA ou dados | Dependências nativas e binários customizados ficam no contêiner |
Para workers longos, revise CPU e custo antes. Cloud Run é forte em serviços orientados a requisições, mas loops em segundo plano exigem mais cuidado.
Serviço Express executável
Cloud Run informa a porta pela variável PORT. O serviço deve ler esse valor, expor /health e encerrar corretamente ao receber SIGTERM.
{
"name": "cloud-run-claude-code-api",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "node --test"
},
"dependencies": {
"express": "^4.19.2"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.10.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
import express from "express";
const app = express();
app.use(express.json());
const requiredEnv = ["DATABASE_URL", "JWT_SECRET"];
for (const key of requiredEnv) {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
}
app.get("/health", (_req, res) => {
res.status(200).json({ ok: true, service: "myapp-api" });
});
app.post("/webhooks/example", (req, res) => {
console.log("webhook_received", {
eventType: req.body?.type ?? "unknown",
receivedAt: new Date().toISOString()
});
res.status(202).json({ accepted: true });
});
app.get("/config-check", (_req, res) => {
res.json({
nodeEnv: process.env.NODE_ENV ?? "development",
hasDatabaseUrl: Boolean(process.env.DATABASE_URL),
hasJwtSecret: Boolean(process.env.JWT_SECRET)
});
});
const port = Number(process.env.PORT ?? 8080);
const server = app.listen(port, () => {
console.log(`listening on ${port}`);
});
process.on("SIGTERM", () => {
console.log("SIGTERM received, closing HTTP server");
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 30000).unref();
});
Teste primeiro localmente:
npm install
DATABASE_URL="postgresql://local" JWT_SECRET="local-secret" npm run dev
curl http://localhost:8080/health
curl -X POST http://localhost:8080/webhooks/example \
-H "Content-Type: application/json" \
-d '{"type":"demo.created"}'
Dockerfile e revisão com Claude Code
Peça ao Claude Code uma revisão de produção, não apenas geração. Os pontos críticos são imagem pequena, dependências de produção, usuário não root, .dockerignore, suporte a PORT e riscos de segurança/custo.
claude -p "
Review and improve this Cloud Run Docker setup.
Requirements:
- Node.js 22 LTS, TypeScript, Express
- production dependencies only in runtime image
- run as a non-root user
- listen on the PORT environment variable
- include .dockerignore
- explain any Cloud Run security or cost risks
Return the final Dockerfile and a short review checklist.
"
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /app/dist ./dist
USER appuser
EXPOSE 8080
CMD ["node", "--max-old-space-size=384", "dist/index.js"]
node_modules
dist
.env
.env.*
*.log
.git
.gitignore
Dockerfile
README.md
docker build -t myapp-api:local .
docker run --rm -p 8080:8080 \
-e PORT=8080 \
-e DATABASE_URL="postgresql://local" \
-e JWT_SECRET="local-secret" \
myapp-api:local
curl http://localhost:8080/health
Artifact Registry e primeiro deploy
Artifact Registry armazena a imagem Docker. Configure a autenticação do host regional, crie o repositório e envie a imagem.
PROJECT_ID="my-project-123"
REGION="asia-northeast1"
REPOSITORY="myapp"
SERVICE="myapp-api"
IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/api:v1.0.0"
gcloud config set project "$PROJECT_ID"
gcloud services enable run.googleapis.com artifactregistry.googleapis.com secretmanager.googleapis.com logging.googleapis.com
gcloud artifacts repositories create "$REPOSITORY" \
--repository-format=docker \
--location="$REGION" \
--description="Docker images for myapp"
gcloud auth configure-docker "$REGION-docker.pkg.dev"
docker build -t "$IMAGE" .
docker push "$IMAGE"
Crie uma service account de runtime antes do deploy.
gcloud iam service-accounts create myapp-run \
--display-name="Cloud Run runtime for myapp"
SERVICE_ACCOUNT="myapp-run@$PROJECT_ID.iam.gserviceaccount.com"
gcloud run deploy "$SERVICE" \
--image "$IMAGE" \
--region "$REGION" \
--platform managed \
--service-account "$SERVICE_ACCOUNT" \
--memory 512Mi \
--cpu 1 \
--concurrency 80 \
--min-instances 0 \
--max-instances 20 \
--allow-unauthenticated \
--set-env-vars NODE_ENV=production \
--port 8080
Para API interna, remova --allow-unauthenticated e conceda Cloud Run Invoker apenas aos chamadores necessários.
Secret Manager, IAM e segurança
Não grave secrets em --set-env-vars. Guarde no Secret Manager e injete no Cloud Run.
echo -n "postgresql://user:password@host:5432/app" | \
gcloud secrets create DATABASE_URL --data-file=-
echo -n "replace-with-long-random-value" | \
gcloud secrets create JWT_SECRET --data-file=-
gcloud secrets add-iam-policy-binding DATABASE_URL \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding JWT_SECRET \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/secretmanager.secretAccessor"
gcloud run services update "$SERVICE" \
--region "$REGION" \
--set-secrets "DATABASE_URL=DATABASE_URL:latest,JWT_SECRET=JWT_SECRET:latest"
Prefira IAM por secret em vez de papéis amplos no projeto. Isso reduz o impacto de erro de configuração.
Concorrência, instâncias mínimas e custo
Concorrência é o número máximo de requisições simultâneas por instância. Valores altos reduzem instâncias, mas podem saturar o banco ou APIs externas.
gcloud run services update "$SERVICE" \
--region "$REGION" \
--concurrency 40 \
--min-instances 1 \
--max-instances 20 \
--cpu-throttling
Use min-instances 0 em desenvolvimento e serviços de baixo tráfego. Use 1 ou mais se um webhook ou API de produção não tolera cold start. Comece webhooks em 20-40, BFF/API em 40-80, e ajuste com latência p95, conexões de banco e erros.
Logs e rollback
Cloud Run envia logs de requisição, contêiner e sistema para Cloud Logging. Em Node.js, stdout/stderr costuma ser suficiente.
gcloud run services logs read "$SERVICE" \
--region "$REGION" \
--limit 20
gcloud logging read \
"resource.type=cloud_run_revision AND resource.labels.service_name=$SERVICE" \
--limit 20 \
--format=json
Cada deploy ou mudança cria uma revision imutável. Para voltar, mova o tráfego para uma revision estável.
gcloud run revisions list \
--service "$SERVICE" \
--region "$REGION"
gcloud run services update-traffic "$SERVICE" \
--region "$REGION" \
--to-revisions myapp-api-00012-abc=100
Prompt de revisão antes de produção:
claude -p "
Act as a Cloud Run deployment reviewer.
Review package.json, Dockerfile, src/index.ts, and the gcloud commands below.
Find blockers before production:
- Cloud Run PORT handling
- SIGTERM graceful shutdown
- non-root container
- Secret Manager usage
- service account and IAM least privilege
- concurrency, min instances, max instances, and cost risks
- Cloud Logging observability
- rollback command for the previous revision
Return: critical issues, recommended fixes, and commands to verify after deploy.
"
Armadilhas comuns
Imagens grandes atrasam build, push e cold start. Secrets em variáveis comuns podem aparecer no histórico ou no CI. min-instances 1 reduz latência, mas cobra mesmo sem tráfego. Concorrência alta pode derrubar banco e SaaS externos. Rollback deve ser ensaiado antes de um incidente.
Referências e próximo passo
- Documentação do Cloud Run
- Deploy de imagens de contêiner no Cloud Run
- Autenticação Docker do Artifact Registry
- Configurar secrets no Cloud Run
- Logs do Cloud Run
- Rollback e migração de tráfego no Cloud Run
- Integração Docker com Claude Code
- Guia Claude Code AWS ECS/Fargate
- Boas práticas de segurança com Claude Code
Cloud Run é um meio-termo prático: mais flexível que funções puras e mais leve que ECS ou Kubernetes. Para transformar prompts, revisões e guardrails de deploy em processo de equipe, veja treinamento e consultoria Claude Code.
Nota de verificação prática
O exemplo foi verificado com npm run dev, acesso Docker a /health, suporte a PORT e validação de variáveis obrigatórias. Antes de produção, confira permissões reais do Artifact Registry, IAM do Secret Manager, consultas no Cloud Logging e um ensaio de rollback com update-traffic.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.