# Tutorials
Start-to-finish walkthroughs for the tasks people get stuck on. Every step links to official docs. Read them and verify before running anything in production.
πΈ Put a real hard cap on GCP spending
A GCP budget only sends alerts β it never stops usage. This wires a budget to a function that actually disables billing when you cross the line. Use it on sandbox/learning projects where an overrun is worse than an outage.
- Create a budget with a threshold. Billing β Budgets & alerts β Create budget, scope it to the project, set the amount and alert thresholds (e.g. 50/90/100%).
- Give the budget a Pub/Sub topic. On the budget's last step, expand Manage notifications and connect it to a new Pub/Sub topic (e.g.
billing-alerts). This publishes a message every time spend updates. - Deploy a function subscribed to that topic that calls the Cloud Billing API to detach the billing account:
# the function's core call (Node/Python client shown in the docs) billing.projects.updateBillingInfo({ name: 'projects/PROJECT_ID', requestBody: { billingAccountName: '' } // '' = disable billing }) - Grant the function's service account the billing role. It needs
roles/billing.projectManager(or Billing Account Admin) to detach billing. - Test with a low budget. Temporarily set a tiny amount, confirm the function fires and billing detaches, then reset. Re-enabling billing later is a one-click console action.
Docs: disable billing with notifications Β· related fix: budgets don't cap spending.
π Create a least-privilege service account (no keys)
The safe default for workloads: a dedicated service account with only the roles it needs, attached to the resource β never a downloaded JSON key.
- Create the service account:
gcloud iam service-accounts create my-app-sa \ --display-name="My app runtime SA" - Grant only the roles it needs, scoped as tightly as possible (project shown; prefer resource-level where supported):
gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:my-app-sa@PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/storage.objectViewer" - Attach the SA to the workload instead of exporting a key. For Cloud Run:
--service-account=my-app-sa@β¦; for a VM: set it as the instance service account; for GKE: use Workload Identity. - Let the client libraries find it automatically. On Google infrastructure, Application Default Credentials use the attached SA with zero config β no key file to leak.
- Verify what it can do with the Policy Troubleshooter or:
gcloud projects get-iam-policy PROJECT_ID \ --flatten="bindings[].members" \ --filter="bindings.members:my-app-sa@PROJECT_ID.iam.gserviceaccount.com"
Docs: best practices for service accounts Β· Workload Identity Federation.
β‘ Deploy a container to Cloud Run (without the PORT error)
The #1 Cloud Run failure is "container failed to start and listen on the port." Get it right the first time.
- Listen on
0.0.0.0:$PORT. Read the port from the environment β never hardcode3000/8080or bind to127.0.0.1:const port = process.env.PORT || 8080; app.listen(port, '0.0.0.0'); - Enable the APIs once:
gcloud services enable run.googleapis.com \ artifactregistry.googleapis.com cloudbuild.googleapis.com - Deploy straight from source (Cloud Build builds & pushes the image for you):
gcloud run deploy my-service \ --source . --region REGION \ --service-account=my-app-sa@PROJECT_ID.iam.gserviceaccount.com - Choose access. Add
--allow-unauthenticatedfor a public site; omit it and grant callersroles/run.invokerfor a private service. - If it still fails to start, read the startup stack trace in Cloud Logging, test the image locally with
docker run -e PORT=8080 -p 8080:8080 IMAGE, and raise the startup timeout if init is slow.
Docs: deploying to Cloud Run Β· container runtime contract Β· related fix: PORT error.
βΈοΈ Connect kubectl to a new GKE cluster
Fresh cluster, and kubectl says "you must be logged in." You just need credentials and the auth plugin.
- Install the auth plugin (required since kubectl 1.26):
gcloud components install gke-gcloud-auth-plugin - Fetch cluster credentials into kubeconfig:
gcloud container clusters get-credentials CLUSTER \ --region REGION --project PROJECT_ID - Verify connectivity:
kubectl get nodes - If you get 403s, grant your identity
roles/container.developer(orcontainer.admin) and make sure the Kubernetes Engine API is enabled.
Docs: cluster access for kubectl.
π§ Set up Workspace email on a brand-new domain
Get mail flowing to Gmail in the right order so you don't chase a "not receiving mail" ghost.
- Verify domain ownership. Admin console setup tool gives you a
google-site-verificationTXT record β paste the exact value into your registrar's DNS (host@). - Replace MX records. Delete any legacy MX entries and add Google's single MX host (
smtp.google.com, priority 1 on new setups). Only Google's MX should remain. - Activate Gmail from the setup tool once records are in place. DNS can take up to 72 hours to propagate.
- Confirm delivery by sending a test message in and out. If inbound fails, re-check that no old MX record is still present.
- Then set up email authentication (next tutorial) before you send anything at volume.
Docs: verify your domain Β· set up MX records.
βοΈ Set up SPF, DKIM & DMARC (in the right order)
Order matters. Authenticate first, enforce last β or you'll bounce your own mail. Since Feb 2024 Gmail requires this for reliable delivery.
- SPF β publish exactly one TXT record listing every sender:
Keep total DNS lookups under 10. No Admin-console step needed.
v=spf1 include:_spf.google.com ~all - DKIM β Admin console β Apps β Gmail β Authenticate email β generate a 2048-bit key β add the TXT record at your host β return and click Start authentication.
- Let both settle for at least 48 hours and confirm they pass (check message headers:
spf=pass,dkim=pass). - DMARC β start at monitor only:
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com - Review reports, then tighten to
p=quarantineand eventuallyp=rejectonce you've confirmed all legitimate mail aligns.
Docs: SPF Β· DKIM Β· DMARC Β· related fixes: email auth.
π Roll out 2-Step Verification without mass lockouts
Enforcing 2SV before people enroll locks everyone out at once. Sequence it.
- Prep admins first. Ensure 2+ super admins have recovery email/phone set, so a bad rollout can't lock you out of the console.
- Turn on allowance, not enforcement. Security β Authentication β 2-Step Verification β allow users to turn it on.
- Announce + set an enrollment period. Give users a deadline (e.g. 2β4 weeks) to enroll a second factor.
- Enforce per-OU after the window, starting with a pilot OU. Prefer a grace period for new users.
- Plan for lost factors. Admins can issue backup codes (Directory β Users β Security). Note: "only security key" mode disables self-service backup codes β the admin must generate them.
Docs: deploy 2SV Β· avoid lockouts Β· related fix: 2SV lockouts.
π Use shared drives so files survive offboarding
My Drive files are owned by the person β delete the account and they can orphan. Shared drives are owned by the org. Set teams up this way from day one.
- Enable shared drives for the right OU: Admin console β Apps β Google Workspace β Drive and Docs β Sharing settings / Manage shared drives.
- Create a shared drive per team and add members by group (not individuals) so membership tracks your directory.
- Set member roles deliberately β Manager, Content manager, Contributor, Commenter, Viewer.
- Move existing team files in. Ownership transfers to the shared drive (org-owned) so departures don't orphan anything.
- For any remaining My Drive files, transfer ownership before deleting a user β the delete flow offers a data transfer step.
Docs: set up shared drives Β· manage shared drives.