My declaragent deploy got 403
The single most common Cloud Run failure. gcloud run services replace returns HTTP 403 + a cryptic Permission 'run.services.create' denied.
Flowchart
The three IAM roles that matter
| Role | Why it's needed |
|---|---|
roles/run.admin | Create + update the Cloud Run service. |
roles/iam.serviceAccountUser | Attach a service account to a Cloud Run revision. |
roles/secretmanager.secretAccessor | Bind ${secret:...} refs into env vars. |
There is no built-in IAM preflight today — check the roles yourself before deploying:
PRINCIPAL="user:you@example.com"
PROJECT="my-project"
gcloud projects get-iam-policy "$PROJECT" \
--flatten="bindings[].members" \
--filter="bindings.members:$PRINCIPAL" \
--format="value(bindings.role)"
If any of the three roles is missing from the output, grant it (next section) before running gcloud.
Quick fixes
# The deploying principal — usually your gcloud user.
PRINCIPAL="user:you@example.com"
PROJECT="my-project"
gcloud projects add-iam-policy-binding "$PROJECT" \
--member="$PRINCIPAL" --role="roles/run.admin"
gcloud projects add-iam-policy-binding "$PROJECT" \
--member="$PRINCIPAL" --role="roles/iam.serviceAccountUser"
gcloud projects add-iam-policy-binding "$PROJECT" \
--member="$PRINCIPAL" --role="roles/secretmanager.secretAccessor"
After the deploy
declaragent deploy gcp-cloud-run --verify is a post-deploy health check: it runs
gcloud run services describe on the deployed service and probes its /health
endpoint. Use it to confirm a deploy that succeeded is actually serving — it cannot
diagnose a 403 that prevented the service from being created in the first place.
Still stuck
- Run
declaragent deploy gcp-cloud-run --verifyand paste the full output into a GitHub issue, together with theget-iam-policyoutput above. - Confirm the
Dockerfile+service.yamlgenerators emitted the expected secret bindings by inspecting.declaragent/deploy/service.yaml.